1 package cn.home1.oss.lib.webmvc.api;
2
3 import static com.google.common.collect.Sets.newLinkedHashSet;
4 import static java.lang.Boolean.parseBoolean;
5 import static java.util.Arrays.asList;
6 import static java.util.stream.Collectors.toList;
7 import static lombok.AccessLevel.PRIVATE;
8
9 import lombok.AllArgsConstructor;
10 import lombok.NoArgsConstructor;
11 import lombok.NonNull;
12 import lombok.SneakyThrows;
13 import lombok.extern.slf4j.Slf4j;
14
15 import org.apache.commons.io.FileUtils;
16 import org.eclipse.jetty.http.HttpMethod;
17 import org.eclipse.jetty.server.ConnectionFactory;
18 import org.eclipse.jetty.server.Connector;
19 import org.eclipse.jetty.server.ForwardedRequestCustomizer;
20 import org.eclipse.jetty.server.HttpConfiguration;
21 import org.eclipse.jetty.server.HttpConnectionFactory;
22 import org.eclipse.jetty.server.NCSARequestLog;
23 import org.eclipse.jetty.server.RequestLog;
24 import org.eclipse.jetty.server.Server;
25 import org.eclipse.jetty.server.ServerConnector;
26 import org.eclipse.jetty.server.Slf4jRequestLog;
27 import org.eclipse.jetty.server.handler.HandlerCollection;
28 import org.eclipse.jetty.server.handler.MovedContextHandler;
29 import org.eclipse.jetty.server.handler.RequestLogHandler;
30 import org.eclipse.jetty.server.handler.gzip.GzipHandler;
31 import org.eclipse.jetty.util.thread.QueuedThreadPool;
32 import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
33 import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
34 import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
35 import org.springframework.core.env.Environment;
36
37 import java.io.File;
38 import java.util.Collection;
39
40 public interface Jetty9ServerCustomizers {
41
42 @SuppressWarnings("unchecked")
43 static <T> Collection<T> connectorConnectionFactories(final Connector connector,
44 final Class<T> ofType) {
45 final Collection<T> connectionFactories = newLinkedHashSet();
46 final ConnectionFactory defaultConnectionFactory = connector.getDefaultConnectionFactory();
47 if (defaultConnectionFactory != null
48 && ofType.isAssignableFrom(defaultConnectionFactory.getClass())) {
49 connectionFactories.add((T) defaultConnectionFactory);
50 }
51 connectionFactories.addAll(connector.getConnectionFactories().stream()
52 .filter(connectionFactory -> ofType.isAssignableFrom(connectionFactory.getClass()))
53 .map(connectionFactory -> (T) connectionFactory).collect(toList()));
54 return connectionFactories;
55 }
56
57 static <T> Collection<T> serverConnectionFactories(final Server server, final Class<T> ofType) {
58 return newLinkedHashSet(asList(server.getConnectors()).stream()
59 .flatMap(connector -> connectorConnectionFactories(connector, ofType).stream())
60 .collect(toList()));
61 }
62
63 static Collection<ServerConnector> serverConnectors(final Server server) {
64 return newLinkedHashSet(
65 asList(server.getConnectors()).stream().filter(c -> c instanceof ServerConnector)
66 .map(connector -> (ServerConnector) connector).collect(toList()));
67 }
68
69 static JettyEmbeddedServletContainerFactory jetty(
70 final ConfigurableEmbeddedServletContainer container) {
71 if (container instanceof JettyEmbeddedServletContainerFactory) {
72 return (JettyEmbeddedServletContainerFactory) container;
73 } else {
74 return null;
75 }
76 }
77
78 @AllArgsConstructor(access = PRIVATE)
79 class RedirectToHostRoot implements JettyServerCustomizer {
80
81 @NonNull
82 private final String host;
83 @NonNull
84 private final String root;
85
86 @Override
87 public void customize(final Server server) {
88 final HandlerCollection handlers = new HandlerCollection();
89 final MovedContextHandler movedContextHandler = new MovedContextHandler();
90 movedContextHandler.setContextPath("/");
91 movedContextHandler.setNewContextURL(this.root);
92 movedContextHandler.setPermanent(true);
93 movedContextHandler.setDiscardPathInfo(false);
94 movedContextHandler.setDiscardQuery(false);
95 movedContextHandler.setVirtualHosts(new String[]{this.host});
96 handlers.addHandler(movedContextHandler);
97
98 asList(server.getHandlers()).forEach(handlers::addHandler);
99 server.setHandler(handlers);
100 }
101 }
102
103
104
105
106
107
108
109
110
111 static void redirectRootDomainToHostRoot(
112 final ConfigurableEmbeddedServletContainer container, final String host, final String root
113 ) {
114 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
115 if (jetty == null) {
116 return;
117 }
118
119 jetty.addServerCustomizers(new RedirectToHostRoot(host, root));
120 }
121
122 @AllArgsConstructor(access = PRIVATE)
123 class AccessLog implements JettyServerCustomizer {
124
125 private String applicationName;
126
127 @Override
128 public void customize(final Server server) {
129 final HandlerCollection handlers = new HandlerCollection();
130
131 final RequestLog requestLog = ncsaRequestLog(applicationName);
132 final RequestLogHandler logHandler = new RequestLogHandler();
133 logHandler.setRequestLog(requestLog);
134 handlers.addHandler(logHandler);
135
136 asList(server.getHandlers()).forEach(handlers::addHandler);
137 server.setHandler(handlers);
138 }
139
140 public static RequestLog slf4jLog() {
141 final Slf4jRequestLog requestLog = new Slf4jRequestLog();
142 return requestLog;
143 }
144
145 @SneakyThrows
146 public static RequestLog ncsaRequestLog(String applicationName) {
147 final String logPath = "./logs";
148 FileUtils.forceMkdir(new File(logPath));
149
150 final String logPattern = logPath + "/" + applicationName + "_access.yyyy_mm_dd.log";
151 final NCSARequestLog requestLog = new NCSARequestLog(logPattern);
152 requestLog.setLogDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
153 requestLog.setRetainDays(30);
154 requestLog.setAppend(true);
155 requestLog.setExtended(true);
156 requestLog.setLogTimeZone("GMT+8");
157 requestLog.setLogLatency(true);
158 return requestLog;
159 }
160 }
161
162 static void accessLog(final Environment environment,
163 final ConfigurableEmbeddedServletContainer container) {
164 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
165 if (jetty == null) {
166 return;
167 }
168 String applicationName =
169 environment.getProperty("spring.application.name", "$spring.application.name");
170 jetty.addServerCustomizers(new AccessLog(applicationName));
171 }
172
173 @NoArgsConstructor(access = PRIVATE)
174 @Slf4j
175 class Gzip implements JettyServerCustomizer {
176
177 @Override
178 public void customize(final Server server) {
179 try {
180 Class.forName("org.eclipse.jetty.server.handler.gzip.GzipHandler");
181 } catch (final ClassNotFoundException ignored) {
182 log.debug("org.eclipse.jetty.server.handler.gzip.GzipHandler is not in classpath.", ignored);
183 return;
184 }
185
186 final HandlerCollection handlers = new HandlerCollection();
187
188 final GzipHandler gzipHandler = new GzipHandler();
189 gzipHandler.addIncludedMethods(HttpMethod.GET.asString(), HttpMethod.POST.asString(),
190 HttpMethod.PUT.asString(), HttpMethod.DELETE.asString());
191 gzipHandler.setCompressionLevel(5);
192 handlers.addHandler(gzipHandler);
193
194 asList(server.getHandlers()).forEach(handlers::addHandler);
195 server.setHandler(handlers);
196 }
197 }
198
199
200
201
202
203
204
205 static void gzip(
206 final Environment environment, final ConfigurableEmbeddedServletContainer container
207 ) {
208 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
209 final Boolean gzipEnabled =
210 parseBoolean(environment.getProperty("spring.http.gzip.enabled", "false"));
211 if (gzipEnabled && jetty != null) {
212 jetty.addServerCustomizers(new Gzip());
213 }
214 }
215
216 @NoArgsConstructor(access = PRIVATE)
217 class ForwardedRequest implements JettyServerCustomizer {
218
219 @Override
220 public void customize(final Server server) {
221 final Collection<HttpConnectionFactory> httpConnectionFactories =
222 serverConnectionFactories(server, HttpConnectionFactory.class);
223 httpConnectionFactories.forEach(httpConnectionFactory -> {
224 final HttpConfiguration httpConfiguration = httpConnectionFactory.getHttpConfiguration();
225 httpConfiguration.addCustomizer(new ForwardedRequestCustomizer());
226 });
227 }
228 }
229
230 static void forwardedRequest(final Environment environment,
231 final ConfigurableEmbeddedServletContainer container) {
232 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
233 if (jetty != null) {
234 jetty.addServerCustomizers(new ForwardedRequest());
235 }
236 }
237
238 @NoArgsConstructor(access = PRIVATE)
239 class HideServerInfo implements JettyServerCustomizer {
240
241 @Override
242 public void customize(final Server server) {
243 final Collection<HttpConnectionFactory> httpConnectionFactories =
244 serverConnectionFactories(server, HttpConnectionFactory.class);
245 httpConnectionFactories.forEach(httpConnectionFactory -> {
246 final HttpConfiguration httpConfiguration = httpConnectionFactory.getHttpConfiguration();
247 httpConfiguration.setSendServerVersion(false);
248 httpConfiguration.setSendXPoweredBy(false);
249 httpConfiguration.setSendDateHeader(false);
250 });
251 }
252 }
253
254 static void hideServerInfo(final Environment environment,
255 final ConfigurableEmbeddedServletContainer container) {
256 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
257 if (jetty != null) {
258 jetty.addServerCustomizers(new HideServerInfo());
259 }
260 }
261
262 @NoArgsConstructor(access = PRIVATE)
263 class NullSessionIdManager implements JettyServerCustomizer {
264
265 @Override
266 public void customize(final Server server) {
267 server.setSessionIdManager(null);
268 }
269 }
270
271 static void nullSessionIdManager(final Environment environment,
272 final ConfigurableEmbeddedServletContainer container) {
273 final JettyEmbeddedServletContainerFactory jetty = jetty(container);
274 if (jetty != null) {
275 jetty.addServerCustomizers(new NullSessionIdManager());
276 }
277 }
278
279 @Deprecated
280 @AllArgsConstructor
281 class Pool implements JettyServerCustomizer {
282
283
284 private final String maxThreads;
285
286 private final String minThreads;
287
288 private final String idleTimeout;
289
290 @Override
291 public void customize(final Server server) {
292 final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
293 threadPool.setMaxThreads(Integer.parseInt(this.maxThreads));
294 threadPool.setMinThreads(Integer.parseInt(this.minThreads));
295 threadPool.setIdleTimeout(Integer.parseInt(this.idleTimeout));
296 }
297
298 public static Pool smallPool() {
299 return new Pool("1", "8", "60000");
300 }
301 }
302 }