001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.web.embedded.jetty; 018 019import java.net.InetSocketAddress; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.List; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.eclipse.jetty.server.AbstractConnector; 028import org.eclipse.jetty.server.ConnectionFactory; 029import org.eclipse.jetty.server.Handler; 030import org.eclipse.jetty.server.HttpConfiguration; 031import org.eclipse.jetty.server.HttpConnectionFactory; 032import org.eclipse.jetty.server.Server; 033import org.eclipse.jetty.server.ServerConnector; 034import org.eclipse.jetty.server.handler.HandlerWrapper; 035import org.eclipse.jetty.servlet.ServletContextHandler; 036import org.eclipse.jetty.servlet.ServletHolder; 037import org.eclipse.jetty.util.thread.ThreadPool; 038 039import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; 040import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; 041import org.springframework.boot.web.server.WebServer; 042import org.springframework.http.client.reactive.JettyResourceFactory; 043import org.springframework.http.server.reactive.HttpHandler; 044import org.springframework.http.server.reactive.JettyHttpHandlerAdapter; 045import org.springframework.util.Assert; 046import org.springframework.util.StringUtils; 047 048/** 049 * {@link ReactiveWebServerFactory} that can be used to create {@link JettyWebServer}s. 050 * 051 * @author Brian Clozel 052 * @since 2.0.0 053 */ 054public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFactory 055 implements ConfigurableJettyWebServerFactory { 056 057 private static final Log logger = LogFactory 058 .getLog(JettyReactiveWebServerFactory.class); 059 060 /** 061 * The number of acceptor threads to use. 062 */ 063 private int acceptors = -1; 064 065 /** 066 * The number of selector threads to use. 067 */ 068 private int selectors = -1; 069 070 private boolean useForwardHeaders; 071 072 private List<JettyServerCustomizer> jettyServerCustomizers = new ArrayList<>(); 073 074 private JettyResourceFactory resourceFactory; 075 076 private ThreadPool threadPool; 077 078 /** 079 * Create a new {@link JettyServletWebServerFactory} instance. 080 */ 081 public JettyReactiveWebServerFactory() { 082 } 083 084 /** 085 * Create a new {@link JettyServletWebServerFactory} that listens for requests using 086 * the specified port. 087 * @param port the port to listen on 088 */ 089 public JettyReactiveWebServerFactory(int port) { 090 super(port); 091 } 092 093 @Override 094 public void setUseForwardHeaders(boolean useForwardHeaders) { 095 this.useForwardHeaders = useForwardHeaders; 096 } 097 098 @Override 099 public void setAcceptors(int acceptors) { 100 this.acceptors = acceptors; 101 } 102 103 @Override 104 public WebServer getWebServer(HttpHandler httpHandler) { 105 JettyHttpHandlerAdapter servlet = new JettyHttpHandlerAdapter(httpHandler); 106 Server server = createJettyServer(servlet); 107 return new JettyWebServer(server, getPort() >= 0); 108 } 109 110 @Override 111 public void addServerCustomizers(JettyServerCustomizer... customizers) { 112 Assert.notNull(customizers, "Customizers must not be null"); 113 this.jettyServerCustomizers.addAll(Arrays.asList(customizers)); 114 } 115 116 /** 117 * Sets {@link JettyServerCustomizer}s that will be applied to the {@link Server} 118 * before it is started. Calling this method will replace any existing customizers. 119 * @param customizers the Jetty customizers to apply 120 */ 121 public void setServerCustomizers( 122 Collection<? extends JettyServerCustomizer> customizers) { 123 Assert.notNull(customizers, "Customizers must not be null"); 124 this.jettyServerCustomizers = new ArrayList<>(customizers); 125 } 126 127 /** 128 * Returns a mutable collection of Jetty {@link JettyServerCustomizer}s that will be 129 * applied to the {@link Server} before it is created. 130 * @return the Jetty customizers 131 */ 132 public Collection<JettyServerCustomizer> getServerCustomizers() { 133 return this.jettyServerCustomizers; 134 } 135 136 /** 137 * Returns a Jetty {@link ThreadPool} that should be used by the {@link Server}. 138 * @return a Jetty {@link ThreadPool} or {@code null} 139 */ 140 public ThreadPool getThreadPool() { 141 return this.threadPool; 142 } 143 144 /** 145 * Set a Jetty {@link ThreadPool} that should be used by the {@link Server}. If set to 146 * {@code null} (default), the {@link Server} creates a {@link ThreadPool} implicitly. 147 * @param threadPool a Jetty ThreadPool to be used 148 */ 149 public void setThreadPool(ThreadPool threadPool) { 150 this.threadPool = threadPool; 151 } 152 153 @Override 154 public void setSelectors(int selectors) { 155 this.selectors = selectors; 156 } 157 158 /** 159 * Set the {@link JettyResourceFactory} to get the shared resources from. 160 * @param resourceFactory the server resources 161 * @since 2.1.0 162 */ 163 public void setResourceFactory(JettyResourceFactory resourceFactory) { 164 this.resourceFactory = resourceFactory; 165 } 166 167 protected JettyResourceFactory getResourceFactory() { 168 return this.resourceFactory; 169 } 170 171 protected Server createJettyServer(JettyHttpHandlerAdapter servlet) { 172 int port = (getPort() >= 0) ? getPort() : 0; 173 InetSocketAddress address = new InetSocketAddress(getAddress(), port); 174 Server server = new Server(getThreadPool()); 175 server.addConnector(createConnector(address, server)); 176 ServletHolder servletHolder = new ServletHolder(servlet); 177 servletHolder.setAsyncSupported(true); 178 ServletContextHandler contextHandler = new ServletContextHandler(server, "", 179 false, false); 180 contextHandler.addServlet(servletHolder, "/"); 181 server.setHandler(addHandlerWrappers(contextHandler)); 182 JettyReactiveWebServerFactory.logger 183 .info("Server initialized with port: " + port); 184 if (getSsl() != null && getSsl().isEnabled()) { 185 customizeSsl(server, address); 186 } 187 for (JettyServerCustomizer customizer : getServerCustomizers()) { 188 customizer.customize(server); 189 } 190 if (this.useForwardHeaders) { 191 new ForwardHeadersCustomizer().customize(server); 192 } 193 return server; 194 } 195 196 private AbstractConnector createConnector(InetSocketAddress address, Server server) { 197 ServerConnector connector; 198 JettyResourceFactory resourceFactory = getResourceFactory(); 199 if (resourceFactory != null) { 200 connector = new ServerConnector(server, resourceFactory.getExecutor(), 201 resourceFactory.getScheduler(), resourceFactory.getByteBufferPool(), 202 this.acceptors, this.selectors, new HttpConnectionFactory()); 203 } 204 else { 205 connector = new ServerConnector(server, this.acceptors, this.selectors); 206 } 207 connector.setHost(address.getHostString()); 208 connector.setPort(address.getPort()); 209 for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) { 210 if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) { 211 ((HttpConfiguration.ConnectionFactory) connectionFactory) 212 .getHttpConfiguration().setSendServerVersion(false); 213 } 214 } 215 return connector; 216 } 217 218 private Handler addHandlerWrappers(Handler handler) { 219 if (getCompression() != null && getCompression().getEnabled()) { 220 handler = applyWrapper(handler, 221 JettyHandlerWrappers.createGzipHandlerWrapper(getCompression())); 222 } 223 if (StringUtils.hasText(getServerHeader())) { 224 handler = applyWrapper(handler, JettyHandlerWrappers 225 .createServerHeaderHandlerWrapper(getServerHeader())); 226 } 227 return handler; 228 } 229 230 private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) { 231 wrapper.setHandler(handler); 232 return wrapper; 233 } 234 235 private void customizeSsl(Server server, InetSocketAddress address) { 236 new SslServerCustomizer(address, getSsl(), getSslStoreProvider(), getHttp2()) 237 .customize(server); 238 } 239 240}