View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.core5.http.impl.bootstrap;
28  
29  import java.net.InetAddress;
30  import java.util.ArrayList;
31  import java.util.List;
32  
33  import javax.net.ServerSocketFactory;
34  import javax.net.ssl.SSLContext;
35  import javax.net.ssl.SSLParameters;
36  import javax.net.ssl.SSLServerSocketFactory;
37  
38  import org.apache.hc.core5.function.Callback;
39  import org.apache.hc.core5.function.Supplier;
40  import org.apache.hc.core5.http.ClassicHttpResponse;
41  import org.apache.hc.core5.http.ConnectionReuseStrategy;
42  import org.apache.hc.core5.http.ExceptionListener;
43  import org.apache.hc.core5.http.HttpResponseFactory;
44  import org.apache.hc.core5.http.URIScheme;
45  import org.apache.hc.core5.http.config.CharCodingConfig;
46  import org.apache.hc.core5.http.config.Http1Config;
47  import org.apache.hc.core5.http.config.NamedElementChain;
48  import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
49  import org.apache.hc.core5.http.impl.Http1StreamListener;
50  import org.apache.hc.core5.http.impl.HttpProcessors;
51  import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection;
52  import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory;
53  import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
54  import org.apache.hc.core5.http.impl.io.HttpService;
55  import org.apache.hc.core5.http.io.HttpConnectionFactory;
56  import org.apache.hc.core5.http.io.HttpFilterHandler;
57  import org.apache.hc.core5.http.io.HttpRequestHandler;
58  import org.apache.hc.core5.http.io.HttpServerRequestHandler;
59  import org.apache.hc.core5.http.io.SocketConfig;
60  import org.apache.hc.core5.http.io.ssl.DefaultTlsSetupHandler;
61  import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
62  import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
63  import org.apache.hc.core5.http.io.support.HttpServerExpectationFilter;
64  import org.apache.hc.core5.http.io.support.HttpServerFilterChainElement;
65  import org.apache.hc.core5.http.io.support.HttpServerFilterChainRequestHandler;
66  import org.apache.hc.core5.http.io.support.TerminalServerFilter;
67  import org.apache.hc.core5.http.protocol.HttpProcessor;
68  import org.apache.hc.core5.http.protocol.LookupRegistry;
69  import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
70  import org.apache.hc.core5.http.protocol.UriPatternType;
71  import org.apache.hc.core5.net.InetAddressUtils;
72  import org.apache.hc.core5.util.Args;
73  
74  /**
75   * {@link HttpServer} bootstrap.
76   *
77   * @since 4.4
78   */
79  public class ServerBootstrap {
80  
81      private final List<HandlerEntry<HttpRequestHandler>> handlerList;
82      private final List<FilterEntry<HttpFilterHandler>> filters;
83      private String canonicalHostName;
84      private LookupRegistry<HttpRequestHandler> lookupRegistry;
85      private int listenerPort;
86      private InetAddress localAddress;
87      private SocketConfig socketConfig;
88      private Http1Config http1Config;
89      private CharCodingConfig charCodingConfig;
90      private HttpProcessor httpProcessor;
91      private ConnectionReuseStrategy connStrategy;
92      private HttpResponseFactory<ClassicHttpResponse> responseFactory;
93      private ServerSocketFactory serverSocketFactory;
94      private SSLContext sslContext;
95      private Callback<SSLParameters> sslSetupHandler;
96      private HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
97      private ExceptionListener exceptionListener;
98      private Http1StreamListener streamListener;
99  
100     private ServerBootstrap() {
101         this.handlerList = new ArrayList<>();
102         this.filters = new ArrayList<>();
103     }
104 
105     public static ServerBootstrap bootstrap() {
106         return new ServerBootstrap();
107     }
108 
109     /**
110      * Sets canonical name (fully qualified domain name) of the server.
111      *
112      * @since 5.0
113      */
114     public final ServerBootstrap setCanonicalHostName(final String canonicalHostName) {
115         this.canonicalHostName = canonicalHostName;
116         return this;
117     }
118 
119     /**
120      * Sets listener port number.
121      */
122     public final ServerBootstrap setListenerPort(final int listenerPort) {
123         this.listenerPort = listenerPort;
124         return this;
125     }
126 
127     /**
128      * Assigns local interface for the listener.
129      */
130     public final ServerBootstrap setLocalAddress(final InetAddress localAddress) {
131         this.localAddress = localAddress;
132         return this;
133     }
134 
135     /**
136      * Sets socket configuration.
137      */
138     public final ServerBootstrap setSocketConfig(final SocketConfig socketConfig) {
139         this.socketConfig = socketConfig;
140         return this;
141     }
142 
143     /**
144      * Sets connection configuration.
145      */
146     public final ServerBootstrap setHttp1Config(final Http1Config http1Config) {
147         this.http1Config = http1Config;
148         return this;
149     }
150 
151     /**
152      * Sets connection configuration.
153      */
154     public final ServerBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) {
155         this.charCodingConfig = charCodingConfig;
156         return this;
157     }
158 
159     /**
160      * Assigns {@link HttpProcessor} instance.
161      */
162     public final ServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) {
163         this.httpProcessor = httpProcessor;
164         return this;
165     }
166 
167     /**
168      * Assigns {@link ConnectionReuseStrategy} instance.
169      */
170     public final ServerBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) {
171         this.connStrategy = connStrategy;
172         return this;
173     }
174 
175     /**
176      * Assigns {@link HttpResponseFactory} instance.
177      */
178     public final ServerBootstrap setResponseFactory(final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
179         this.responseFactory = responseFactory;
180         return this;
181     }
182 
183     /**
184      * Assigns {@link LookupRegistry} instance.
185      */
186     public final ServerBootstrap setLookupRegistry(final LookupRegistry<HttpRequestHandler> lookupRegistry) {
187         this.lookupRegistry = lookupRegistry;
188         return this;
189     }
190 
191     /**
192      * Registers the given {@link HttpRequestHandler} as a default handler for URIs
193      * matching the given pattern.
194      *
195      * @param uriPattern the pattern to register the handler for.
196      * @param requestHandler the handler.
197      */
198     public final ServerBootstrap register(final String uriPattern, final HttpRequestHandler requestHandler) {
199         Args.notBlank(uriPattern, "URI pattern");
200         Args.notNull(requestHandler, "Supplier");
201         handlerList.add(new HandlerEntry<>(null, uriPattern, requestHandler));
202         return this;
203     }
204 
205     /**
206      * Registers the given {@link HttpRequestHandler} as a handler for URIs
207      * matching the given host and the pattern.
208      *
209      * @param hostname
210      * @param uriPattern the pattern to register the handler for.
211      * @param requestHandler the handler.
212      */
213     public final ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final HttpRequestHandler requestHandler) {
214         Args.notBlank(hostname, "Hostname");
215         Args.notBlank(uriPattern, "URI pattern");
216         Args.notNull(requestHandler, "Supplier");
217         handlerList.add(new HandlerEntry<>(hostname, uriPattern, requestHandler));
218         return this;
219     }
220 
221     /**
222      * Assigns {@link HttpConnectionFactory} instance.
223      */
224     public final ServerBootstrap setConnectionFactory(
225             final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory) {
226         this.connectionFactory = connectionFactory;
227         return this;
228     }
229 
230     /**
231      * Assigns {@link javax.net.ServerSocketFactory} instance.
232      */
233     public final ServerBootstrap setServerSocketFactory(final ServerSocketFactory serverSocketFactory) {
234         this.serverSocketFactory = serverSocketFactory;
235         return this;
236     }
237 
238     /**
239      * Assigns {@link javax.net.ssl.SSLContext} instance.
240      * <p>
241      * Please note this value can be overridden by the {@link #setServerSocketFactory(
242      *   javax.net.ServerSocketFactory)} method.
243      */
244     public final ServerBootstrap setSslContext(final SSLContext sslContext) {
245         this.sslContext = sslContext;
246         return this;
247     }
248 
249     /**
250      * Assigns {@link Callback} for {@link SSLParameters}.
251      */
252     public final ServerBootstrap setSslSetupHandler(final Callback<SSLParameters> sslSetupHandler) {
253         this.sslSetupHandler = sslSetupHandler;
254         return this;
255     }
256 
257     /**
258      * Assigns {@link ExceptionListener} instance.
259      */
260     public final ServerBootstrap setExceptionListener(final ExceptionListener exceptionListener) {
261         this.exceptionListener = exceptionListener;
262         return this;
263     }
264 
265     /**
266      * Assigns {@link ExceptionListener} instance.
267      */
268     public final ServerBootstrap setStreamListener(final Http1StreamListener streamListener) {
269         this.streamListener = streamListener;
270         return this;
271     }
272 
273     /**
274      * Adds the filter before the filter with the given name.
275      */
276     public final ServerBootstrap addFilterBefore(final String existing, final String name, final HttpFilterHandler filterHandler) {
277         Args.notBlank(existing, "Existing");
278         Args.notBlank(name, "Name");
279         Args.notNull(filterHandler, "Filter handler");
280         filters.add(new FilterEntry<>(FilterEntry.Postion.BEFORE, name, filterHandler, existing));
281         return this;
282     }
283 
284     /**
285      * Adds the filter after the filter with the given name.
286      */
287     public final ServerBootstrap addFilterAfter(final String existing, final String name, final HttpFilterHandler filterHandler) {
288         Args.notBlank(existing, "Existing");
289         Args.notBlank(name, "Name");
290         Args.notNull(filterHandler, "Filter handler");
291         filters.add(new FilterEntry<>(FilterEntry.Postion.AFTER, name, filterHandler, existing));
292         return this;
293     }
294 
295     /**
296      * Replace an existing filter with the given name with new filter.
297      */
298     public final ServerBootstrap replaceFilter(final String existing, final HttpFilterHandler filterHandler) {
299         Args.notBlank(existing, "Existing");
300         Args.notNull(filterHandler, "Filter handler");
301         filters.add(new FilterEntry<>(FilterEntry.Postion.REPLACE, existing, filterHandler, existing));
302         return this;
303     }
304 
305     /**
306      * Add an filter to the head of the processing list.
307      */
308     public final ServerBootstrap addFilterFirst(final String name, final HttpFilterHandler filterHandler) {
309         Args.notNull(name, "Name");
310         Args.notNull(filterHandler, "Filter handler");
311         filters.add(new FilterEntry<>(FilterEntry.Postion.FIRST, name, filterHandler, null));
312         return this;
313     }
314 
315     /**
316      * Add an filter to the tail of the processing list.
317      */
318     public final ServerBootstrap addFilterLast(final String name, final HttpFilterHandler filterHandler) {
319         Args.notNull(name, "Name");
320         Args.notNull(filterHandler, "Filter handler");
321         filters.add(new FilterEntry<>(FilterEntry.Postion.LAST, name, filterHandler, null));
322         return this;
323     }
324 
325     public HttpServer create() {
326         final RequestHandlerRegistry<HttpRequestHandler> handlerRegistry = new RequestHandlerRegistry<>(
327                 canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(),
328                 new Supplier<LookupRegistry<HttpRequestHandler>>() {
329 
330                     @Override
331                     public LookupRegistry<HttpRequestHandler> get() {
332                         return lookupRegistry != null ? lookupRegistry :
333                                 UriPatternType.<HttpRequestHandler>newMatcher(UriPatternType.URI_PATTERN);
334                     }
335 
336                 });
337         for (final HandlerEntry<HttpRequestHandler> entry: handlerList) {
338             handlerRegistry.register(entry.hostname, entry.uriPattern, entry.handler);
339         }
340 
341         final HttpServerRequestHandler requestHandler;
342         if (!filters.isEmpty()) {
343             final NamedElementChain<HttpFilterHandler> filterChainDefinition = new NamedElementChain<>();
344             filterChainDefinition.addLast(
345                     new TerminalServerFilter(
346                             handlerRegistry,
347                             this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE),
348                     StandardFilter.MAIN_HANDLER.name());
349             filterChainDefinition.addFirst(
350                     new HttpServerExpectationFilter(),
351                     StandardFilter.EXPECT_CONTINUE.name());
352 
353             for (final FilterEntry<HttpFilterHandler> entry: filters) {
354                 switch (entry.postion) {
355                     case AFTER:
356                         filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name);
357                         break;
358                     case BEFORE:
359                         filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name);
360                         break;
361                     case REPLACE:
362                         filterChainDefinition.replace(entry.existing, entry.filterHandler);
363                         break;
364                     case FIRST:
365                         filterChainDefinition.addFirst(entry.filterHandler, entry.name);
366                         break;
367                     case LAST:
368                         // Don't add last, after TerminalServerFilter, as that does not delegate to the chain
369                         // Instead, add the filter just before it, making it effectively the last filter
370                         filterChainDefinition.addBefore(StandardFilter.MAIN_HANDLER.name(), entry.filterHandler, entry.name);
371                         break;
372                 }
373             }
374 
375             NamedElementChain<HttpFilterHandler>.Node current = filterChainDefinition.getLast();
376             HttpServerFilterChainElement filterChain = null;
377             while (current != null) {
378                 filterChain = new HttpServerFilterChainElement(current.getValue(), filterChain);
379                 current = current.getPrevious();
380             }
381             requestHandler = new HttpServerFilterChainRequestHandler(filterChain);
382         } else {
383             requestHandler = new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(
384                     handlerRegistry,
385                     this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE));
386         }
387 
388         final HttpService/io/HttpService.html#HttpService">HttpService httpService = new HttpService(
389                 this.httpProcessor != null ? this.httpProcessor : HttpProcessors.server(),
390                 requestHandler,
391                 this.connStrategy != null ? this.connStrategy : DefaultConnectionReuseStrategy.INSTANCE,
392                 this.streamListener);
393 
394         ServerSocketFactory serverSocketFactoryCopy = this.serverSocketFactory;
395         if (serverSocketFactoryCopy == null) {
396             if (this.sslContext != null) {
397                 serverSocketFactoryCopy = this.sslContext.getServerSocketFactory();
398             } else {
399                 serverSocketFactoryCopy = ServerSocketFactory.getDefault();
400             }
401         }
402 
403         HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactoryCopy = this.connectionFactory;
404         if (connectionFactoryCopy == null) {
405             final String scheme = serverSocketFactoryCopy instanceof SSLServerSocketFactory ? URIScheme.HTTPS.id : URIScheme.HTTP.id;
406             connectionFactoryCopy = new DefaultBHttpServerConnectionFactory(scheme, this.http1Config, this.charCodingConfig);
407         }
408 
409         return new HttpServer(
410                 this.listenerPort > 0 ? this.listenerPort : 0,
411                 httpService,
412                 this.localAddress,
413                 this.socketConfig != null ? this.socketConfig : SocketConfig.DEFAULT,
414                 serverSocketFactoryCopy,
415                 connectionFactoryCopy,
416                 sslSetupHandler != null ? sslSetupHandler : new DefaultTlsSetupHandler(),
417                 this.exceptionListener != null ? this.exceptionListener : ExceptionListener.NO_OP);
418     }
419 
420 }