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.SSLServerSocketFactory;
36  
37  import org.apache.hc.core5.http.ClassicHttpResponse;
38  import org.apache.hc.core5.http.ConnectionReuseStrategy;
39  import org.apache.hc.core5.http.ExceptionListener;
40  import org.apache.hc.core5.http.HttpResponseFactory;
41  import org.apache.hc.core5.http.URIScheme;
42  import org.apache.hc.core5.http.config.CharCodingConfig;
43  import org.apache.hc.core5.http.config.H1Config;
44  import org.apache.hc.core5.http.config.NamedElementChain;
45  import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
46  import org.apache.hc.core5.http.impl.Http1StreamListener;
47  import org.apache.hc.core5.http.impl.HttpProcessors;
48  import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection;
49  import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory;
50  import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
51  import org.apache.hc.core5.http.impl.io.HttpService;
52  import org.apache.hc.core5.http.io.HttpConnectionFactory;
53  import org.apache.hc.core5.http.io.HttpFilterHandler;
54  import org.apache.hc.core5.http.io.HttpRequestHandler;
55  import org.apache.hc.core5.http.io.HttpServerRequestHandler;
56  import org.apache.hc.core5.http.io.SocketConfig;
57  import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
58  import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
59  import org.apache.hc.core5.http.io.support.HttpServerExpectationFilter;
60  import org.apache.hc.core5.http.io.support.HttpServerFilterChainElement;
61  import org.apache.hc.core5.http.io.support.HttpServerFilterChainRequestHandler;
62  import org.apache.hc.core5.http.io.support.TerminalServerFilter;
63  import org.apache.hc.core5.http.protocol.HttpProcessor;
64  import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
65  import org.apache.hc.core5.http.protocol.UriPatternType;
66  import org.apache.hc.core5.net.InetAddressUtils;
67  import org.apache.hc.core5.util.Args;
68  
69  /**
70   * {@link HttpServer} bootstrap.
71   *
72   * @since 4.4
73   */
74  public class ServerBootstrap {
75  
76      private final List<HandlerEntry<HttpRequestHandler>> handlerList;
77      private final List<FilterEntry<HttpFilterHandler>> filters;
78      private String canonicalHostName;
79      private UriPatternType uriPatternType;
80      private int listenerPort;
81      private InetAddress localAddress;
82      private SocketConfig socketConfig;
83      private H1Config h1Config;
84      private CharCodingConfig charCodingConfig;
85      private HttpProcessor httpProcessor;
86      private ConnectionReuseStrategy connStrategy;
87      private HttpResponseFactory<ClassicHttpResponse> responseFactory;
88      private ServerSocketFactory serverSocketFactory;
89      private SSLContext sslContext;
90      private SSLServerSetupHandler sslSetupHandler;
91      private HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory;
92      private ExceptionListener exceptionListener;
93      private Http1StreamListener streamListener;
94  
95      private ServerBootstrap() {
96          this.handlerList = new ArrayList<>();
97          this.filters = new ArrayList<>();
98      }
99  
100     public static ServerBootstrap bootstrap() {
101         return new ServerBootstrap();
102     }
103 
104     /**
105      * Sets canonical name (fully qualified domain name) of the server.
106      *
107      * @since 5.0
108      */
109     public final ServerBootstrap setCanonicalHostName(final String canonicalHostName) {
110         this.canonicalHostName = canonicalHostName;
111         return this;
112     }
113 
114     /**
115      * Sets listener port number.
116      */
117     public final ServerBootstrap setListenerPort(final int listenerPort) {
118         this.listenerPort = listenerPort;
119         return this;
120     }
121 
122     /**
123      * Assigns local interface for the listener.
124      */
125     public final ServerBootstrap setLocalAddress(final InetAddress localAddress) {
126         this.localAddress = localAddress;
127         return this;
128     }
129 
130     /**
131      * Sets socket configuration.
132      */
133     public final ServerBootstrap setSocketConfig(final SocketConfig socketConfig) {
134         this.socketConfig = socketConfig;
135         return this;
136     }
137 
138     /**
139      * Sets connection configuration.
140      */
141     public final ServerBootstrap setH1Config(final H1Config h1Config) {
142         this.h1Config = h1Config;
143         return this;
144     }
145 
146     /**
147      * Sets connection configuration.
148      */
149     public final ServerBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) {
150         this.charCodingConfig = charCodingConfig;
151         return this;
152     }
153 
154     /**
155      * Assigns {@link HttpProcessor} instance.
156      */
157     public final ServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) {
158         this.httpProcessor = httpProcessor;
159         return this;
160     }
161 
162     /**
163      * Assigns {@link ConnectionReuseStrategy} instance.
164      */
165     public final ServerBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) {
166         this.connStrategy = connStrategy;
167         return this;
168     }
169 
170     /**
171      * Assigns {@link HttpResponseFactory} instance.
172      */
173     public final ServerBootstrap setResponseFactory(final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
174         this.responseFactory = responseFactory;
175         return this;
176     }
177 
178     /**
179      * Assigns {@link UriPatternType} for handler registration.
180      */
181     public final ServerBootstrap  setUriPatternType(final UriPatternType uriPatternType) {
182         this.uriPatternType = uriPatternType;
183         return this;
184     }
185 
186     /**
187      * Registers the given {@link HttpRequestHandler} as a default handler for URIs
188      * matching the given pattern.
189      *
190      * @param uriPattern the pattern to register the handler for.
191      * @param requestHandler the handler.
192      */
193     public final ServerBootstrap register(final String uriPattern, final HttpRequestHandler requestHandler) {
194         Args.notBlank(uriPattern, "URI pattern");
195         Args.notNull(requestHandler, "Supplier");
196         handlerList.add(new HandlerEntry<>(null, uriPattern, requestHandler));
197         return this;
198     }
199 
200     /**
201      * Registers the given {@link HttpRequestHandler} as a handler for URIs
202      * matching the given host and the pattern.
203      *
204      * @param hostname
205      * @param uriPattern the pattern to register the handler for.
206      * @param requestHandler the handler.
207      */
208     public final ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final HttpRequestHandler requestHandler) {
209         Args.notBlank(hostname, "Hostname");
210         Args.notBlank(uriPattern, "URI pattern");
211         Args.notNull(requestHandler, "Supplier");
212         handlerList.add(new HandlerEntry<>(hostname, uriPattern, requestHandler));
213         return this;
214     }
215 
216     /**
217      * Assigns {@link HttpConnectionFactory} instance.
218      */
219     public final ServerBootstrap setConnectionFactory(
220             final HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactory) {
221         this.connectionFactory = connectionFactory;
222         return this;
223     }
224 
225     /**
226      * Assigns {@link SSLServerSetupHandler} instance.
227      */
228     public final ServerBootstrap setSslSetupHandler(final SSLServerSetupHandler sslSetupHandler) {
229         this.sslSetupHandler = sslSetupHandler;
230         return this;
231     }
232 
233     /**
234      * Assigns {@link javax.net.ServerSocketFactory} instance.
235      */
236     public final ServerBootstrap setServerSocketFactory(final ServerSocketFactory serverSocketFactory) {
237         this.serverSocketFactory = serverSocketFactory;
238         return this;
239     }
240 
241     /**
242      * Assigns {@link javax.net.ssl.SSLContext} instance.
243      * <p>
244      * Please note this value can be overridden by the {@link #setServerSocketFactory(
245      *   javax.net.ServerSocketFactory)} method.
246      */
247     public final ServerBootstrap setSslContext(final SSLContext sslContext) {
248         this.sslContext = sslContext;
249         return this;
250     }
251 
252     /**
253      * Assigns {@link ExceptionListener} instance.
254      */
255     public final ServerBootstrap setExceptionListener(final ExceptionListener exceptionListener) {
256         this.exceptionListener = exceptionListener;
257         return this;
258     }
259 
260     /**
261      * Assigns {@link ExceptionListener} instance.
262      */
263     public final ServerBootstrap setStreamListener(final Http1StreamListener streamListener) {
264         this.streamListener = streamListener;
265         return this;
266     }
267 
268     /**
269      * Adds the filter before the filter with the given name.
270      */
271     public final ServerBootstrap addFilterBefore(final String existing, final String name, final HttpFilterHandler filterHandler) {
272         Args.notBlank(existing, "Existing");
273         Args.notBlank(name, "Name");
274         Args.notNull(filterHandler, "Filter handler");
275         filters.add(new FilterEntry<>(FilterEntry.Postion.BEFORE, name, filterHandler, existing));
276         return this;
277     }
278 
279     /**
280      * Adds the filter after the filter with the given name.
281      */
282     public final ServerBootstrap addFilterAfter(final String existing, final String name, final HttpFilterHandler filterHandler) {
283         Args.notBlank(existing, "Existing");
284         Args.notBlank(name, "Name");
285         Args.notNull(filterHandler, "Filter handler");
286         filters.add(new FilterEntry<>(FilterEntry.Postion.AFTER, name, filterHandler, existing));
287         return this;
288     }
289 
290     /**
291      * Replace an existing filter with the given name with new filter.
292      */
293     public final ServerBootstrap replaceFilter(final String existing, final HttpFilterHandler filterHandler) {
294         Args.notBlank(existing, "Existing");
295         Args.notNull(filterHandler, "Filter handler");
296         filters.add(new FilterEntry<>(FilterEntry.Postion.REPLACE, existing, filterHandler, existing));
297         return this;
298     }
299 
300     /**
301      * Add an filter to the head of the processing list.
302      */
303     public final ServerBootstrap addFilterFirst(final String name, final HttpFilterHandler filterHandler) {
304         Args.notNull(name, "Name");
305         Args.notNull(filterHandler, "Filter handler");
306         filters.add(new FilterEntry<>(FilterEntry.Postion.FIRST, name, filterHandler, null));
307         return this;
308     }
309 
310     /**
311      * Add an filter to the tail of the processing list.
312      */
313     public final ServerBootstrap addFilterLast(final String name, final HttpFilterHandler filterHandler) {
314         Args.notNull(name, "Name");
315         Args.notNull(filterHandler, "Filter handler");
316         filters.add(new FilterEntry<>(FilterEntry.Postion.LAST, name, filterHandler, null));
317         return this;
318     }
319 
320     public HttpServer create() {
321         final RequestHandlerRegistry<HttpRequestHandler> handlerRegistry = new RequestHandlerRegistry<>(
322                 canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(),
323                 uriPatternType);
324         for (final HandlerEntry<HttpRequestHandler> entry: handlerList) {
325             handlerRegistry.register(entry.hostname, entry.uriPattern, entry.handler);
326         }
327 
328         final HttpServerRequestHandler requestHandler;
329         if (!filters.isEmpty()) {
330             final NamedElementChain<HttpFilterHandler> filterChainDefinition = new NamedElementChain<>();
331             filterChainDefinition.addLast(
332                     new TerminalServerFilter(
333                             handlerRegistry,
334                             this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE),
335                     StandardFilters.MAIN_HANDLER.name());
336             filterChainDefinition.addFirst(
337                     new HttpServerExpectationFilter(),
338                     StandardFilters.EXPECT_CONTINUE.name());
339 
340             for (final FilterEntry<HttpFilterHandler> entry: filters) {
341                 switch (entry.postion) {
342                     case AFTER:
343                         filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name);
344                         break;
345                     case BEFORE:
346                         filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name);
347                         break;
348                     case REPLACE:
349                         filterChainDefinition.replace(entry.existing, entry.filterHandler);
350                         break;
351                     case FIRST:
352                         filterChainDefinition.addFirst(entry.filterHandler, entry.name);
353                         break;
354                     case LAST:
355                         filterChainDefinition.addLast(entry.filterHandler, entry.name);
356                         break;
357                 }
358             }
359 
360             NamedElementChain<HttpFilterHandler>.Node current = filterChainDefinition.getLast();
361             HttpServerFilterChainElement filterChain = null;
362             while (current != null) {
363                 filterChain = new HttpServerFilterChainElement(current.getValue(), filterChain);
364                 current = current.getPrevious();
365             }
366             requestHandler = new HttpServerFilterChainRequestHandler(filterChain);
367         } else {
368             requestHandler = new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(
369                     handlerRegistry,
370                     this.responseFactory != null ? this.responseFactory : DefaultClassicHttpResponseFactory.INSTANCE));
371         }
372 
373         final HttpService httpService = new HttpService(
374                 this.httpProcessor != null ? this.httpProcessor : HttpProcessors.server(),
375                 requestHandler,
376                 this.connStrategy != null ? this.connStrategy : DefaultConnectionReuseStrategy.INSTANCE,
377                 this.streamListener);
378 
379         ServerSocketFactory serverSocketFactoryCopy = this.serverSocketFactory;
380         if (serverSocketFactoryCopy == null) {
381             if (this.sslContext != null) {
382                 serverSocketFactoryCopy = this.sslContext.getServerSocketFactory();
383             } else {
384                 serverSocketFactoryCopy = ServerSocketFactory.getDefault();
385             }
386         }
387 
388         HttpConnectionFactory<? extends DefaultBHttpServerConnection> connectionFactoryCopy = this.connectionFactory;
389         if (connectionFactoryCopy == null) {
390             final String scheme = serverSocketFactoryCopy instanceof SSLServerSocketFactory ? URIScheme.HTTPS.id : URIScheme.HTTP.id;
391             connectionFactoryCopy = new DefaultBHttpServerConnectionFactory(scheme, this.h1Config, this.charCodingConfig);
392         }
393 
394         return new HttpServer(
395                 this.listenerPort > 0 ? this.listenerPort : 0,
396                 httpService,
397                 this.localAddress,
398                 this.socketConfig != null ? this.socketConfig : SocketConfig.DEFAULT,
399                 serverSocketFactoryCopy,
400                 connectionFactoryCopy,
401                 this.sslSetupHandler,
402                 this.exceptionListener != null ? this.exceptionListener : ExceptionListener.NO_OP);
403     }
404 
405 }