diff --git a/pom.xml b/pom.xml
index 92ee39e4..957d57d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.7.10
+ 2.7.18
@@ -20,8 +20,7 @@
UTF-8
UTF-8
- 3.6.0
- 11
+ 3.8.0
2.4
2.4
@@ -32,14 +31,14 @@
11
- 2.7.10
+ 2.7.18
italiangrid_storm-webdav
italiangrid
https://sonarcloud.io
- 0.4.6.v20220506
+ 3.3.2
2.7.1.7
2.3
@@ -50,14 +49,13 @@
4.2.2
4.2.1
- 31.1-jre
+ 32.0.0-jre
1.0.5.1
2.3.3.RELEASE
6.0.2
5.5.1
- 1.72
@@ -70,8 +68,7 @@
org.apache.maven.plugins
maven-compiler-plugin
-
- 11
+ 11
@@ -99,7 +96,6 @@
src/assembly/tarball.xml
- storm-webdav
@@ -107,6 +103,9 @@
single
+
+ false
+
@@ -190,13 +189,6 @@
org.springframework.boot
spring-boot-starter-actuator
-
-
-
- org.apache.logging.log4j
- log4j-api
-
-
@@ -256,6 +248,12 @@
org.springframework.boot
spring-boot-starter-test
test
+
+
+ com.vaadin.external.google
+ android-json
+
+
@@ -326,11 +324,6 @@
metrics-core
-
- io.dropwizard.metrics
- metrics-jvm
-
-
io.dropwizard.metrics
metrics-jetty9
@@ -348,45 +341,30 @@
- org.italiangrid
- jetty-utils
- ${jetty-utils.version}
-
-
- javax.activation
- activation
-
-
- javax.mail
- mail
-
-
- org.eclipse.jetty.aggregate
- jetty-all
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
+ org.eclipse.jetty.http2
+ http2-server
- org.bouncycastle
- bcpkix-jdk18on
- ${bouncycastle.version}
+ org.eclipse.jetty
+ jetty-alpn-conscrypt-server
- org.bouncycastle
- bcprov-jdk18on
- ${bouncycastle.version}
+ org.slf4j
+ slf4j-api
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+
+ org.italiangrid
+ voms-api-java
+ ${voms-api-java.version}
+
ch.qos.logback
diff --git a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java
index 9b3a0e03..e22bbf09 100644
--- a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java
+++ b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfiguration.java
@@ -33,6 +33,8 @@ public interface ServiceConfiguration {
public long getTrustAnchorsRefreshIntervalInSeconds();
+ public int getMinConnections();
+
public int getMaxConnections();
public int getMaxQueueSize();
diff --git a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java
index b6e09321..bf9d9e60 100644
--- a/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java
+++ b/src/main/java/org/italiangrid/storm/webdav/config/ServiceConfigurationProperties.java
@@ -260,10 +260,13 @@ public static class ConnectorProperties {
int securePort = 8443;
@Positive
- int maxConnections = 200;
+ int minConnections = 50;
@Positive
- int maxQueueSize = 512;
+ int maxConnections = 300;
+
+ @Positive
+ int maxQueueSize = 900;
@Positive
int maxIdleTimeMsec = 30000;
@@ -292,6 +295,14 @@ public void setSecurePort(int securePort) {
this.securePort = securePort;
}
+ public int getMinConnections() {
+ return minConnections;
+ }
+
+ public void setMinConnections(int minConnections) {
+ this.minConnections = minConnections;
+ }
+
public int getMaxConnections() {
return maxConnections;
}
@@ -732,55 +743,51 @@ public long getTrustAnchorsRefreshIntervalInSeconds() {
return getTls().getTrustAnchorsRefreshIntervalSecs();
}
+ @Override
+ public int getMinConnections() {
+ return getConnector().getMinConnections();
+ }
@Override
public int getMaxConnections() {
return getConnector().getMaxConnections();
}
-
@Override
public int getMaxQueueSize() {
return getConnector().getMaxQueueSize();
}
-
@Override
public int getConnectorMaxIdleTimeInMsec() {
return getConnector().getMaxIdleTimeMsec();
}
-
@Override
public String getSAConfigDir() {
return getSa().getConfigDir();
}
-
@Override
public boolean enableVOMapFiles() {
return getVoMapFiles().isEnabled();
}
-
@Override
public String getVOMapFilesConfigDir() {
return getVoMapFiles().getConfigDir();
}
-
@Override
public long getVOMapFilesRefreshIntervalInSeconds() {
return getVoMapFiles().getRefreshIntervalSec();
}
-
@Override
public boolean isAuthorizationDisabled() {
return getAuthz().isDisabled();
}
-
@Override
public boolean requireClientCertificateAuthentication() {
return getTls().isRequireClientCert();
diff --git a/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java b/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java
index 4e798c4e..82c028e0 100644
--- a/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java
+++ b/src/main/java/org/italiangrid/storm/webdav/server/DefaultJettyServerCustomizer.java
@@ -32,7 +32,6 @@
import org.italiangrid.storm.webdav.config.ServiceConfiguration;
import org.italiangrid.storm.webdav.config.ServiceConfigurationProperties;
import org.italiangrid.storm.webdav.config.StorageAreaConfiguration;
-import org.italiangrid.utils.jetty.TLSServerConnectorBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ServerProperties;
diff --git a/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java b/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java
index b2fb8dfb..2e336aa6 100644
--- a/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java
+++ b/src/main/java/org/italiangrid/storm/webdav/server/DefaultWebServerFactory.java
@@ -15,14 +15,16 @@
*/
package org.italiangrid.storm.webdav.server;
+import java.util.concurrent.ArrayBlockingQueue;
+
import org.italiangrid.storm.webdav.config.ServiceConfiguration;
-import org.italiangrid.utils.jetty.ThreadPoolBuilder;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool;
public class DefaultWebServerFactory
implements WebServerFactoryCustomizer {
@@ -42,18 +44,18 @@ public DefaultWebServerFactory(ServiceConfiguration configuration,
this.metricRegistry = registry;
}
+ private InstrumentedQueuedThreadPool getInstrumentedThreadPool() {
+ InstrumentedQueuedThreadPool tPool = new InstrumentedQueuedThreadPool(metricRegistry, configuration.getMaxConnections(),
+ configuration.getMinConnections(), configuration.getConnectorMaxIdleTimeInMsec(),
+ new ArrayBlockingQueue(configuration.getMaxQueueSize()), "storm.http");
+ tPool.setName("thread-pool");
+ return tPool;
+ }
+
@Override
public void customize(JettyServletWebServerFactory factory) {
- factory.setThreadPool(ThreadPoolBuilder.instance()
- .withMaxRequestQueueSize(configuration.getMaxQueueSize())
- .withMaxThreads(serverProperties.getJetty().getThreads().getMax())
- .withMinThreads(serverProperties.getJetty().getThreads().getMin())
- .registry(metricRegistry)
- .withPrefix("storm.http")
- .withName("thread-pool")
- .build());
-
+ factory.setThreadPool(getInstrumentedThreadPool());
factory.addServerCustomizers(serverCustomizer);
}
diff --git a/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java b/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java
new file mode 100644
index 00000000..8d05acb8
--- /dev/null
+++ b/src/main/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderError.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023.
+ *
+ * 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
+ *
+ * http://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.italiangrid.storm.webdav.server;
+
+public class TLSConnectorBuilderError extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public TLSConnectorBuilderError(Throwable cause) {
+ super(cause);
+ }
+
+ public TLSConnectorBuilderError(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TLSConnectorBuilderError(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java
new file mode 100644
index 00000000..67379b58
--- /dev/null
+++ b/src/main/java/org/italiangrid/storm/webdav/server/TLSServerConnectorBuilder.java
@@ -0,0 +1,664 @@
+/**
+ * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023.
+ *
+ * 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
+ *
+ * http://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.italiangrid.storm.webdav.server;
+
+import static java.util.Objects.isNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.conscrypt.OpenSSLProvider;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+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.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.jetty9.InstrumentedConnectionFactory;
+
+import eu.emi.security.authn.x509.X509CertChainValidatorExt;
+import eu.emi.security.authn.x509.helpers.ssl.SSLTrustManager;
+import eu.emi.security.authn.x509.impl.PEMCredential;
+
+/**
+ * A builder that configures a Jetty server TLS connector integrated with CANL
+ * {@link X509CertChainValidatorExt} certificate validation services.
+ *
+ */
+public class TLSServerConnectorBuilder {
+
+ /**
+ * Conscrypt provider name.
+ */
+ public static final String CONSCRYPT_PROVIDER = "Conscrypt";
+
+ /**
+ * Default service certificate file.
+ */
+ public static final String DEFAULT_CERTIFICATE_FILE = "/etc/grid-security/hostcert.pem";
+
+ /**
+ * Default service certificate private key file.
+ */
+ public static final String DEFAULT_CERTIFICATE_KEY_FILE = "/etc/grid-security/hostcert.pem";
+
+ /**
+ * The port for this connector.
+ */
+ private int port;
+
+ /**
+ * The certificate file location.
+ */
+ private String certificateFile = DEFAULT_CERTIFICATE_FILE;
+
+ /**
+ * The certificate private key file location.
+ */
+ private String certificateKeyFile = DEFAULT_CERTIFICATE_KEY_FILE;
+
+ /**
+ * The password to decrypt the certificate private key file.
+ */
+ private char[] certicateKeyPassword = null;
+
+ /**
+ * The certificate validator used by this connector builder.
+ */
+ private final X509CertChainValidatorExt certificateValidator;
+
+ /**
+ * Whether client auth will be required for this connector.
+ */
+ private boolean tlsNeedClientAuth = false;
+
+ /**
+ * Whether cluent auth is supported for this connector.
+ */
+ private boolean tlsWantClientAuth = true;
+
+ /**
+ * Supported SSL protocols.
+ */
+ private String[] includeProtocols;
+
+ /**
+ * Disabled SSL protocols.
+ */
+ private String[] excludeProtocols;
+
+ /**
+ * Supported cipher suites.
+ */
+ private String[] includeCipherSuites;
+
+ /**
+ * Disabled cipher suites.
+ */
+ private String[] excludeCipherSuites;
+
+ /**
+ * The HTTP configuration for the connector being created.
+ */
+ private HttpConfiguration httpConfiguration;
+
+ /**
+ * The key manager to use for the connector being created.
+ */
+ private KeyManager keyManager;
+
+ /**
+ * The server for which the connector is being created.
+ */
+ private final Server server;
+
+ /**
+ * The metric name to associate to the connector being built.
+ */
+ private String metricName;
+
+ /**
+ * The metric registry.
+ */
+ private MetricRegistry registry;
+
+ /**
+ * Whether the Conscrypt provider should be used instead of the default JSSE implementation
+ */
+ private boolean useConscrypt = false;
+
+
+ /**
+ * Whether HTTP/2 should be configured
+ */
+ private boolean enableHttp2 = false;
+
+ /**
+ * Which TLS protocol string should be used
+ */
+ private String tlsProtocol = "TLSv1.2";
+
+ /**
+ * Custom TLS hostname verifier
+ */
+ private HostnameVerifier hostnameVerifier = null;
+
+ /**
+ * Disable JSSE hostname verification
+ */
+ private boolean disableJsseHostnameVerification = false;
+
+ /**
+ * Number of acceptors threads for the connector
+ */
+ private int acceptors = -1;
+
+ /**
+ * Number of selector threads for the connector
+ */
+ private int selectors = -1;
+
+ /**
+ * Returns an instance of the {@link TLSServerConnectorBuilder}.
+ *
+ * @param s the {@link Server} for which the connector is being created
+ * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates
+ * @return an instance of the {@link TLSServerConnectorBuilder}
+ */
+ public static TLSServerConnectorBuilder instance(Server s,
+ X509CertChainValidatorExt certificateValidator) {
+
+ return new TLSServerConnectorBuilder(s, certificateValidator);
+ }
+
+ /**
+ * Private ctor.
+ *
+ * @param s the {@link Server} for which the connector is being created
+ * @param certificateValidator a {@link X509CertChainValidatorExt} used to validate certificates
+ */
+ private TLSServerConnectorBuilder(Server s, X509CertChainValidatorExt certificateValidator) {
+
+ if (s == null) {
+ throw new IllegalArgumentException("Server cannot be null");
+ }
+
+ if (certificateValidator == null) {
+ throw new IllegalArgumentException("certificateValidator cannot be null");
+ }
+
+ this.server = s;
+ this.certificateValidator = certificateValidator;
+ }
+
+ private void credentialsSanityChecks() {
+
+ checkFileExistsAndIsReadable(new File(certificateFile), "Error accessing certificate file");
+
+ checkFileExistsAndIsReadable(new File(certificateKeyFile),
+ "Error accessing certificate key file");
+
+ }
+
+ private void loadCredentials() {
+
+ credentialsSanityChecks();
+
+ PEMCredential serviceCredentials = null;
+
+ try {
+
+ serviceCredentials =
+ new PEMCredential(certificateKeyFile, certificateFile, certicateKeyPassword);
+
+ } catch (KeyStoreException | CertificateException | IOException e) {
+
+ throw new TLSConnectorBuilderError("Error setting up service credentials", e);
+ }
+
+ keyManager = serviceCredentials.getKeyManager();
+ }
+
+ /**
+ * Configures SSL session parameters for the jetty {@link SslContextFactory}.
+ *
+ * @param contextFactory the {@link SslContextFactory} being configured
+ */
+ private void configureContextFactory(SslContextFactory.Server contextFactory) {
+
+ if (excludeProtocols != null) {
+ contextFactory.setExcludeProtocols(excludeProtocols);
+ }
+
+ if (includeProtocols != null) {
+ contextFactory.setIncludeProtocols(includeProtocols);
+ }
+
+ if (excludeCipherSuites != null) {
+ contextFactory.setExcludeCipherSuites(excludeCipherSuites);
+ }
+
+ if (includeCipherSuites != null) {
+ contextFactory.setIncludeCipherSuites(includeCipherSuites);
+ }
+
+ contextFactory.setWantClientAuth(tlsWantClientAuth);
+ contextFactory.setNeedClientAuth(tlsNeedClientAuth);
+
+ if (useConscrypt) {
+ contextFactory.setProvider(CONSCRYPT_PROVIDER);
+ } else {
+ contextFactory.setProvider(BouncyCastleProvider.PROVIDER_NAME);
+ }
+
+ if (hostnameVerifier != null) {
+ contextFactory.setHostnameVerifier(hostnameVerifier);
+ }
+
+ if (disableJsseHostnameVerification) {
+ contextFactory.setEndpointIdentificationAlgorithm(null);
+ }
+
+ }
+
+ /**
+ * Builds a default {@link HttpConfiguration} for the TLS-enabled connector being created
+ *
+ * @return the default {@link HttpConfiguration}
+ */
+ private HttpConfiguration defaultHttpConfiguration() {
+
+ HttpConfiguration httpsConfig = new HttpConfiguration();
+
+ httpsConfig.setSecureScheme("https");
+
+ httpsConfig.setSecurePort(port);
+
+ httpsConfig.setOutputBufferSize(32768);
+ httpsConfig.setRequestHeaderSize(8192);
+ httpsConfig.setResponseHeaderSize(8192);
+
+ httpsConfig.setSendServerVersion(true);
+ httpsConfig.setSendDateHeader(false);
+
+ httpsConfig.addCustomizer(new SecureRequestCustomizer());
+
+ return httpsConfig;
+
+ }
+
+ /**
+ * Gives access to the {@link HttpConfiguration} used for the TLS-enabled connector being created.
+ * If the configuration is not set, it creates it using {@link #defaultHttpConfiguration()}.
+ *
+ * @return the {@link HttpConfiguration} being used for the TLS-enabled connector.
+ */
+ public HttpConfiguration httpConfiguration() {
+
+ if (httpConfiguration == null) {
+ httpConfiguration = defaultHttpConfiguration();
+ }
+
+ return httpConfiguration;
+
+ }
+
+ /**
+ * Sets the port for the connector being created.
+ *
+ * @param port the port for the connector
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withPort(int port) {
+
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * Sets the certificate file for the connector being created.
+ *
+ * @param certificateFile the certificate file
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withCertificateFile(String certificateFile) {
+
+ this.certificateFile = certificateFile;
+ return this;
+ }
+
+ /**
+ * Sets the certificate key file for the connector being created.
+ *
+ * @param certificateKeyFile the certificate key file
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withCertificateKeyFile(String certificateKeyFile) {
+
+ this.certificateKeyFile = certificateKeyFile;
+ return this;
+ }
+
+ /**
+ * The the certificate key password for the connector being built
+ *
+ * @param certificateKeyPassword the certificate key password
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withCertificateKeyPassword(char[] certificateKeyPassword) {
+
+ this.certicateKeyPassword = certificateKeyPassword;
+ return this;
+ }
+
+ /**
+ * Sets the {@link SslContextFactory#setNeedClientAuth(boolean)} parameter for the connector being
+ * created.
+ *
+ * @param needClientAuth true if client authentication is required
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withNeedClientAuth(boolean needClientAuth) {
+
+ this.tlsNeedClientAuth = needClientAuth;
+ return this;
+ }
+
+ /**
+ * Sets the {@link SslContextFactory#setWantClientAuth(boolean)} parameter for the connector being
+ * created.
+ *
+ * @param wantClientAuth true if client authentication is wanted
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withWantClientAuth(boolean wantClientAuth) {
+
+ this.tlsWantClientAuth = wantClientAuth;
+ return this;
+ }
+
+ /**
+ * Sets SSL included protocols. See {@link SslContextFactory#setIncludeProtocols(String...)}.
+ *
+ * @param includeProtocols the array of included protocol names
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withIncludeProtocols(String... includeProtocols) {
+
+ this.includeProtocols = includeProtocols;
+ return this;
+ }
+
+ /**
+ * Sets SSL excluded protocols. See {@link SslContextFactory#setExcludeProtocols(String...)}.
+ *
+ * @param excludeProtocols the array of excluded protocol names
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withExcludeProtocols(String... excludeProtocols) {
+
+ this.excludeProtocols = excludeProtocols;
+ return this;
+ }
+
+ /**
+ * Sets the SSL included cipher suites.
+ *
+ * @param includeCipherSuites the array of included cipher suites.
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withIncludeCipherSuites(String... includeCipherSuites) {
+
+ this.includeCipherSuites = includeCipherSuites;
+ return this;
+ }
+
+ /**
+ * Sets the SSL ecluded cipher suites.
+ *
+ * @param excludeCipherSuites the array of excluded cipher suites.
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withExcludeCipherSuites(String... excludeCipherSuites) {
+
+ this.excludeCipherSuites = excludeCipherSuites;
+ return this;
+ }
+
+ /**
+ * Sets the {@link HttpConfiguration} for the connector being built.
+ *
+ * @param conf the {@link HttpConfiguration} to use
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withHttpConfiguration(HttpConfiguration conf) {
+
+ this.httpConfiguration = conf;
+ return this;
+ }
+
+ /**
+ * Sets the {@link KeyManager} for the connector being built.
+ *
+ * @param km the {@link KeyManager} to use
+ * @return this builder
+ */
+ public TLSServerConnectorBuilder withKeyManager(KeyManager km) {
+
+ this.keyManager = km;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withConscrypt(boolean conscryptEnabled) {
+ this.useConscrypt = conscryptEnabled;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withHttp2(boolean http2Enabled) {
+ this.enableHttp2 = http2Enabled;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder metricRegistry(MetricRegistry registry) {
+ this.registry = registry;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder metricName(String metricName) {
+ this.metricName = metricName;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withTlsProtocol(String tlsProtocol) {
+ this.tlsProtocol = tlsProtocol;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withHostnameVerifier(HostnameVerifier verifier) {
+ this.hostnameVerifier = verifier;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withDisableJsseHostnameVerification(
+ boolean disableJsseHostnameVerification) {
+ this.disableJsseHostnameVerification = disableJsseHostnameVerification;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withAcceptors(int acceptors) {
+ this.acceptors = acceptors;
+ return this;
+ }
+
+ public TLSServerConnectorBuilder withSelectors(int selectors) {
+ this.selectors = selectors;
+ return this;
+ }
+
+ private SSLContext buildSSLContext() {
+
+ SSLContext sslCtx;
+
+ try {
+
+ KeyManager[] kms = new KeyManager[] {keyManager};
+ SSLTrustManager tm = new SSLTrustManager(certificateValidator);
+
+ if (useConscrypt) {
+
+ if (isNull(Security.getProvider(CONSCRYPT_PROVIDER))) {
+ Security.addProvider(new OpenSSLProvider());
+ }
+
+ sslCtx = SSLContext.getInstance(tlsProtocol, CONSCRYPT_PROVIDER);
+ } else {
+ sslCtx = SSLContext.getInstance(tlsProtocol);
+ }
+
+ sslCtx.init(kms, new TrustManager[] {tm}, null);
+
+ } catch (NoSuchAlgorithmException e) {
+
+ throw new TLSConnectorBuilderError("TLS protocol not supported: " + e.getMessage(), e);
+ } catch (KeyManagementException e) {
+ throw new TLSConnectorBuilderError(e);
+ } catch (NoSuchProviderException e) {
+ throw new TLSConnectorBuilderError("TLS provider error: " + e.getMessage(), e);
+ }
+
+ return sslCtx;
+ }
+
+ /**
+ * Builds a {@link ServerConnector} based on the {@link TLSServerConnectorBuilder} parameters
+ *
+ * @return a {@link ServerConnector}
+ */
+ public ServerConnector build() {
+
+ if (keyManager == null) {
+ loadCredentials();
+ }
+
+ SSLContext sslContext = buildSSLContext();
+ SslContextFactory.Server cf = new SslContextFactory.Server();
+
+ cf.setSslContext(sslContext);
+
+
+ configureContextFactory(cf);
+
+ if (httpConfiguration == null) {
+ httpConfiguration = defaultHttpConfiguration();
+ }
+
+
+ HttpConnectionFactory httpConnFactory = new HttpConnectionFactory(httpConfiguration);
+ ConnectionFactory connFactory = null;
+
+ if (registry != null) {
+ connFactory = new InstrumentedConnectionFactory(httpConnFactory, registry.timer(metricName));
+ } else {
+ connFactory = httpConnFactory;
+ }
+
+
+ ConnectionFactory h2ConnFactory = null;
+ ServerConnector connector = null;
+
+ if (enableHttp2) {
+
+ HTTP2ServerConnectionFactory h2cf = new HTTP2ServerConnectionFactory(httpConfiguration);
+
+ if (registry != null) {
+ h2ConnFactory = new InstrumentedConnectionFactory(h2cf, registry.timer(metricName));
+ } else {
+ h2ConnFactory = h2cf;
+ }
+ ALPNServerConnectionFactory alpn = createAlpnProtocolFactory(httpConnFactory);
+ cf.setCipherComparator(HTTP2Cipher.COMPARATOR);
+ cf.setUseCipherSuitesOrder(true);
+
+ SslConnectionFactory sslCf = new SslConnectionFactory(cf, alpn.getProtocol());
+
+ connector = new ServerConnector(server, acceptors, selectors, sslCf, alpn, h2ConnFactory,
+ httpConnFactory);
+
+ } else {
+
+ connector = new ServerConnector(server, acceptors, selectors,
+ new SslConnectionFactory(cf, HttpVersion.HTTP_1_1.asString()), connFactory);
+ }
+
+ connector.setPort(port);
+ return connector;
+ }
+
+ private ALPNServerConnectionFactory createAlpnProtocolFactory(
+ HttpConnectionFactory httpConnectionFactory) {
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol(httpConnectionFactory.getProtocol());
+ return alpn;
+ }
+
+
+ /**
+ * Checks that file exists and is readable.
+ *
+ * @param f the {@link File} to be checked
+ * @param prefix A prefix string for the error message, in case the file does not exist and is not
+ * readable
+ * @throws RuntimeException if the file does not exist or is not readable
+ */
+ private void checkFileExistsAndIsReadable(File f, String prefix) {
+
+ String errorMessage = null;
+
+ if (!f.exists()) {
+ errorMessage = "File does not exists";
+ } else if (!f.canRead()) {
+ errorMessage = "File is not readable";
+ } else if (f.isDirectory()) {
+ errorMessage = "File is a directory";
+ }
+
+ if (errorMessage != null) {
+ String msg = String.format("%s: %s [%s]", prefix, errorMessage, f.getAbsolutePath());
+ throw new TLSConnectorBuilderError(msg);
+ }
+
+ }
+}
diff --git a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java
index ce9a7a88..22d43a01 100644
--- a/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java
+++ b/src/main/java/org/italiangrid/storm/webdav/spring/AppConfig.java
@@ -16,7 +16,7 @@
package org.italiangrid.storm.webdav.spring;
import static java.util.Objects.isNull;
-import static org.italiangrid.utils.jetty.TLSServerConnectorBuilder.CONSCRYPT_PROVIDER;
+import static org.italiangrid.storm.webdav.server.TLSServerConnectorBuilder.CONSCRYPT_PROVIDER;
import java.io.IOException;
import java.net.MalformedURLException;
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 6eaaa236..67245d86 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -86,13 +86,19 @@ storm:
port: ${STORM_WEBDAV_HTTP_PORT:8085}
# HTTPS connector port
secure-port: ${STORM_WEBDAV_HTTPS_PORT:8443}
+ # Min concurrent connections
+ min-connections: ${STORM_WEBDAV_MIN_CONNECTIONS:50}
# Max concurrent connections
max-connections: ${STORM_WEBDAV_MAX_CONNECTIONS:300}
# Connection queue size
max-queue-size: ${STORM_WEBDAV_MAX_QUEUE_SIZE:900}
# Connector Maximum idle time (in milliseconds)
- max-idle-time-msec: ${STORM_WEBDAV_CONNECTOR_MAX_IDLE_TIME:30000}
+ max-idle-time-msec: ${STORM_WEBDAV_CONNECTOR_MAX_IDLE_TIME:30000}
output-buffer-size-bytes: ${storm.buffer.file-buffer-size-bytes}
+ # Number of acceptor threads to use. When the value is -1, the default, the number of acceptors is derived from the operating environment.
+ jetty-acceptors: ${STORM_WEBDAV_CONNECTOR_ACCEPTORS:-1}
+ # Number of selector threads to use. When the value is -1, the default, the number of selectors is derived from the operating environment.
+ jetty-selectors: ${STORM_WEBDAV_CONNECTOR_SELECTORS:-1}
tls:
# Path to the service certificate.
diff --git a/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java b/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java
new file mode 100644
index 00000000..003dd1b1
--- /dev/null
+++ b/src/test/java/org/italiangrid/storm/webdav/server/TLSConnectorBuilderTest.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2014-2023.
+ *
+ * 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
+ *
+ * http://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.italiangrid.storm.webdav.server;
+
+import static org.junit.Assert.assertThrows;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.KeyManager;
+
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.italiangrid.storm.webdav.server.util.CANLListener;
+import org.italiangrid.voms.util.CertificateValidatorBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import eu.emi.security.authn.x509.CrlCheckingMode;
+import eu.emi.security.authn.x509.NamespaceCheckingMode;
+import eu.emi.security.authn.x509.OCSPCheckingMode;
+import eu.emi.security.authn.x509.X509CertChainValidatorExt;
+
+@ExtendWith(MockitoExtension.class)
+public class TLSConnectorBuilderTest {
+
+ @Test
+ public void tlsConnectorBuilderErrorTests() {
+
+ new TLSConnectorBuilderError("This is an error!");
+ new TLSConnectorBuilderError("This is an error!", new RuntimeException());
+ new TLSConnectorBuilderError(new RuntimeException());
+ }
+
+ @Test
+ public void illegalArgumentExceptionThrown() {
+
+ Server server = Mockito.mock(Server.class);
+ X509CertChainValidatorExt validator = Mockito.mock(X509CertChainValidatorExt.class);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ TLSServerConnectorBuilder.instance(null, validator);
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ TLSServerConnectorBuilder.instance(server, null);
+ });
+ assertThrows(IllegalArgumentException.class, () -> {
+ TLSServerConnectorBuilder.instance(null, null);
+ });
+ }
+
+ private X509CertChainValidatorExt getValidator() {
+
+ CANLListener l = new org.italiangrid.storm.webdav.server.util.CANLListener();
+ CertificateValidatorBuilder builder = new CertificateValidatorBuilder();
+
+ long refreshInterval = TimeUnit.SECONDS.toMillis(3600);
+
+ return builder.namespaceChecks(NamespaceCheckingMode.EUGRIDPMA_AND_GLOBUS_REQUIRE)
+ .crlChecks(CrlCheckingMode.IF_VALID)
+ .ocspChecks(OCSPCheckingMode.IGNORE)
+ .lazyAnchorsLoading(false)
+ .storeUpdateListener(l)
+ .validationErrorListener(l)
+ .trustAnchorsDir("src/test/resources/trust-anchors")
+ .trustAnchorsUpdateInterval(refreshInterval)
+ .build();
+ }
+
+ @Test
+ public void tlsConnectorBuilderTests() {
+
+ Server server = new Server();
+ X509CertChainValidatorExt validator = getValidator();
+ TLSServerConnectorBuilder builder = TLSServerConnectorBuilder.instance(server, validator);
+ HttpConfiguration httpConfiguration = builder.httpConfiguration();
+ KeyManager keyManager = Mockito.mock(KeyManager.class);
+ builder.withPort(1234)
+ .withCertificateFile("fake-certificate")
+ .withCertificateKeyFile("fake-key")
+ .withCertificateKeyPassword("secret".toCharArray())
+ .withHttpConfiguration(httpConfiguration)
+ .withKeyManager(keyManager)
+ .withExcludeCipherSuites("one", "two")
+ .withIncludeCipherSuites("three", "four")
+ .withIncludeProtocols("protocol", "another-protocol")
+ .withExcludeProtocols("another-more-protocol")
+ .withHostnameVerifier(new NoopHostnameVerifier())
+ .withConscrypt(false);
+
+ builder.build();
+ }
+}
\ No newline at end of file