4141import org .apache .zookeeper .metrics .MetricsProviderLifeCycleException ;
4242import org .apache .zookeeper .metrics .Summary ;
4343import org .apache .zookeeper .metrics .SummarySet ;
44+ import org .apache .zookeeper .server .admin .UnifiedConnectionFactory ;
45+ import org .eclipse .jetty .http .HttpVersion ;
46+ import org .eclipse .jetty .server .HttpConfiguration ;
47+ import org .eclipse .jetty .server .HttpConnectionFactory ;
48+ import org .eclipse .jetty .server .SecureRequestCustomizer ;
4449import org .eclipse .jetty .server .Server ;
4550import org .eclipse .jetty .server .ServerConnector ;
4651import org .eclipse .jetty .servlet .ServletContextHandler ;
@@ -84,6 +89,7 @@ public class PrometheusMetricsProvider implements MetricsProvider {
8489 private boolean wantClientAuth = true ; // Secure default
8590 private String enabledProtocols ;
8691 private String cipherSuites ;
92+ private int httpVersion ;
8793
8894 // Constants for configuration
8995 public static final String HTTP_HOST = "httpHost" ;
@@ -101,7 +107,14 @@ public class PrometheusMetricsProvider implements MetricsProvider {
101107 public static final String SSL_WANT_CLIENT_AUTH = "ssl.want.client.auth" ;
102108 public static final String SSL_ENABLED_PROTOCOLS = "ssl.enabledProtocols" ;
103109 public static final String SSL_ENABLED_CIPHERS = "ssl.ciphersuites" ;
110+ public static final String HTTP_VERSION = "httpVersion" ;
104111 public static final int SCAN_INTERVAL = 60 * 10 ; // 10 minutes
112+ public static final int DEFAULT_HTTP_VERSION = 11 ; // based on HttpVersion.java in jetty
113+ /**
114+ * The time, in seconds, that the browser should remember that a host is only to be accessed using HTTPS.
115+ * Seconds in a day.
116+ */
117+ public static final int DEFAULT_STS_MAX_AGE = 1 * 24 * 60 * 60 ;
105118
106119 /**
107120 * Custom servlet to disable the TRACE method for security reasons.
@@ -139,6 +152,7 @@ public void configure(Properties configuration) throws MetricsProviderLifeCycleE
139152 this .wantClientAuth = Boolean .parseBoolean (configuration .getProperty (SSL_WANT_CLIENT_AUTH , "true" ));
140153 this .enabledProtocols = configuration .getProperty (SSL_ENABLED_PROTOCOLS );
141154 this .cipherSuites = configuration .getProperty (SSL_ENABLED_CIPHERS );
155+ this .httpVersion = Integer .getInteger (HTTP_VERSION , DEFAULT_HTTP_VERSION );
142156 }
143157
144158 // Validate that at least one port is configured.
@@ -171,23 +185,49 @@ public void start() throws MetricsProviderLifeCycleException {
171185 int acceptors = 1 ;
172186 int selectors = 1 ;
173187
174- // Configure HTTP connector if enabled
175- if (this .httpPort != -1 ) {
176- ServerConnector httpConnector = new ServerConnector (server , acceptors , selectors );
177- httpConnector .setPort (this .httpPort );
178- httpConnector .setHost (this .host );
179- server .addConnector (httpConnector );
180- }
188+ ServerConnector connector = null ;
189+
190+ if (this .httpPort != -1 && this .httpsPort != -1 && this .httpPort == this .httpsPort ) {
191+ // Set Strict-Transport-Security HTTP response header.
192+ SecureRequestCustomizer customizer = new SecureRequestCustomizer ();
193+ customizer .setStsMaxAge (DEFAULT_STS_MAX_AGE );
194+ // Strict-Transport-Security HTTP header should apply to all subdomains of the host's domain as well.
195+ customizer .setStsIncludeSubDomains (true );
196+
197+ HttpConfiguration config = new HttpConfiguration ();
198+ config .setSecureScheme ("https" );
199+ config .addCustomizer (customizer );
181200
182- // Configure HTTPS connector if enabled
183- if (this .httpsPort != -1 ) {
184201 SslContextFactory .Server sslContextFactory = createSslContextFactory ();
185- KeyStoreScanner keystoreScanner = new KeyStoreScanner (sslContextFactory );
186- keystoreScanner .setScanInterval (SCAN_INTERVAL );
187- server .addBean (keystoreScanner );
188- server .addConnector (createSslConnector (server , acceptors , selectors , sslContextFactory ));
202+ setKeyStoreScanner (sslContextFactory );
203+
204+ String nextProtocol = HttpVersion .fromVersion (httpVersion ).asString ();
205+ connector = new ServerConnector (server ,
206+ new UnifiedConnectionFactory (sslContextFactory , nextProtocol ),
207+ new HttpConnectionFactory (config ));
208+ connector .setPort (this .httpPort );
209+ connector .setHost (this .host );
210+ LOG .info ("Created unified ServerConnector for host: {}, httpPort: {}" , host , httpPort );
211+ } else {
212+ // Configure HTTP connector if enabled
213+ if (this .httpPort != -1 ) {
214+ connector = new ServerConnector (server , acceptors , selectors );
215+ connector .setPort (this .httpPort );
216+ connector .setHost (this .host );
217+ LOG .info ("Created HTTP ServerConnector for host: {}, httpPort: {}" , host , httpPort );
218+ }
219+
220+ // Configure HTTPS connector if enabled
221+ if (this .httpsPort != -1 ) {
222+ SslContextFactory .Server sslContextFactory = createSslContextFactory ();
223+ setKeyStoreScanner (sslContextFactory );
224+ connector = createSslConnector (server , acceptors , selectors , sslContextFactory );
225+ LOG .info ("Created HTTPS ServerConnector for host: {}, httpsPort: {}" , host , httpsPort );
226+ }
189227 }
190228
229+ server .addConnector (connector );
230+
191231 // Set up the servlet context handler
192232 ServletContextHandler context = new ServletContextHandler ();
193233 context .setContextPath ("/" );
@@ -207,6 +247,12 @@ public void start() throws MetricsProviderLifeCycleException {
207247 }
208248 }
209249
250+ private void setKeyStoreScanner (SslContextFactory .Server sslContextFactory ) {
251+ KeyStoreScanner keystoreScanner = new KeyStoreScanner (sslContextFactory );
252+ keystoreScanner .setScanInterval (SCAN_INTERVAL );
253+ server .addBean (keystoreScanner );
254+ }
255+
210256 /**
211257 * Creates and configures the SslContextFactory for the server.
212258 *
0 commit comments