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