/* * Copyright 2013 NGDATA nv * * 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.lilyproject.container.jetty; import static com.google.common.collect.Iterables.toArray; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.lilyproject.runtime.rapi.ConfRegistry; import org.lilyproject.servletregistry.api.ServletFilterRegistryEntry; import org.lilyproject.servletregistry.api.ServletRegistry; import org.lilyproject.servletregistry.api.ServletRegistryEntry; import org.lilyproject.servletregistry.api.ServletRequestListenerRegistryEntry; import org.mortbay.io.EndPoint; import org.mortbay.jetty.Connector; import org.mortbay.jetty.Handler; import org.mortbay.jetty.HttpFields; import org.mortbay.jetty.Request; import org.mortbay.jetty.Server; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.security.SslSocketConnector; import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.FilterHolder; import org.mortbay.jetty.servlet.ServletHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; public class CustomJettyLauncher { public static final String LILY_SSL_KEYSTORE = "lily.ssl.keystore"; private Log log = LogFactory.getLog(CustomJettyLauncher.class); @Autowired private ServletRegistry servletRegistry; @Autowired private ConfRegistry confRegistry; private Server server; private Context context; public CustomJettyLauncher() throws Exception { log.trace("in constructor custom jetty launcher"); } @PostConstruct public Server getServerInstance() throws Exception { if (server != null) return server; log.trace("starting jetty http server."); server = new Server(); ArrayList<Connector> connectors = Lists.newArrayList(); addSSLConnector(connectors); addPlainConnector(connectors); server.setConnectors(toArray(connectors, Connector.class)); if (sessions()) { context = new Context(server, "/", Context.SESSIONS); } else { context = new Context(server, "/", Context.NO_SESSIONS); } for (ServletRegistryEntry entry: servletRegistry.getEntries()) { ServletHolder servletHolder = new ServletHolder(entry.getServletInstance(context.getServletContext())); for (String pattern: entry.getUrlPatterns()) { context.addServlet(servletHolder, pattern); } } for (ServletFilterRegistryEntry servletFilterEntry : servletRegistry.getFilterEntries()) { FilterHolder filterHolder = new FilterHolder(servletFilterEntry.getServletFilterInstance(context.getServletContext())); for (Map.Entry<String, Integer> patternEntry : servletFilterEntry.getUrlPatterns().entrySet()) { int dispatch = patternEntry.getValue() != null ? patternEntry.getValue() : Handler.DEFAULT; context.addFilter(filterHolder, patternEntry.getKey(), dispatch); } } for (ServletRequestListenerRegistryEntry requestListenerRegistryEntry: servletRegistry.getServletRequestListenerEntries()){ context.addEventListener(requestListenerRegistryEntry.getListenerInstance()); } server.start(); return server; } @PreDestroy public void stopServer() throws Exception { server.stop(); } private void addPlainConnector(List<Connector> connectors) { if (startSSL() && ! allowUnencrypted()) return; SelectChannelConnector selectChannelConnector = new SelectChannelConnector(){ //backport from jetty 8's url scheme forwarding support @Override protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException { super.checkForwardedHeaders(endpoint, request); HttpFields httpFields = request.getConnection().getRequestFields(); String forwardedProto = getLeftMostValue(httpFields.getStringField("X-Forwarded-Proto")); if (forwardedProto != null) { request.setScheme(forwardedProto); } } }; selectChannelConnector.setPort(httpPort()); selectChannelConnector.setForwarded(supportReverseProxy()); if (startSSL()) selectChannelConnector.setConfidentialPort(httpsPort()); connectors.add(selectChannelConnector); } private void addSSLConnector(List<Connector> connectors) { if (!startSSL()) return; SslSocketConnector sslConnector = new SslSocketConnector(){ //backport from jetty 8's url scheme forwarding support @Override protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException { super.checkForwardedHeaders(endpoint, request); HttpFields httpFields = request.getConnection().getRequestFields(); String forwardedProto = getLeftMostValue(httpFields.getStringField("X-Forwarded-Proto")); if (forwardedProto != null) { request.setScheme(forwardedProto); } } }; sslConnector.setPort(httpsPort()); if (StringUtils.hasText(keystore())){ sslConnector.setKeystore(keystore()); sslConnector.setTruststore(truststore()); } sslConnector.setPassword(keystorePassword()); sslConnector.setKeyPassword(keyPassword()); sslConnector.setTrustPassword(truststorePassword()); sslConnector.setForwarded(supportReverseProxy()); connectors.add(sslConnector); } /** * Note: this is disabled by default because if we are not behind a proxy the client can send arbitrary values for * the X-Forwarded-* headers and we would treat them as if they were set by a trusted reverse proxy. */ private boolean supportReverseProxy(){ return confRegistry.getConfiguration("jetty").getChild("supportReverseProxy").getValueAsBoolean(false); } private int httpsPort() { return confRegistry.getConfiguration("jetty").getChild("httpsPort").getValueAsInteger(12443); } private int httpPort() { return confRegistry.getConfiguration("jetty").getChild("httpPort").getValueAsInteger(12060); } private boolean allowUnencrypted() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("allowUnencrypted") .getValueAsBoolean(true); } private boolean startSSL() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("startSSL").getValueAsBoolean(false); } private String keystore() { String keystore = confRegistry.getConfiguration("jetty").getChild("ssl").getChild("keystore").getValue(null); if (keystore == null){ keystore = System.getProperty(LILY_SSL_KEYSTORE); } return keystore; } private String truststore() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("truststore").getValue(keystore()); } private String keystorePassword() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("keystorePassword").getValue("changeit"); } private String truststorePassword() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("truststorePassword").getValue(keystorePassword()); } private String keyPassword() { return confRegistry.getConfiguration("jetty").getChild("ssl").getChild("keyPassword").getValue(keystorePassword()); } private boolean sessions(){ return confRegistry.getConfiguration("jetty").getChild("sessions").getValueAsBoolean(true); } }