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