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