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.util.ArrayList;
30  import java.util.List;
31  
32  import org.apache.hc.core5.function.Callback;
33  import org.apache.hc.core5.function.Decorator;
34  import org.apache.hc.core5.function.Supplier;
35  import org.apache.hc.core5.http.ConnectionReuseStrategy;
36  import org.apache.hc.core5.http.HttpRequestMapper;
37  import org.apache.hc.core5.http.config.CharCodingConfig;
38  import org.apache.hc.core5.http.config.Http1Config;
39  import org.apache.hc.core5.http.config.NamedElementChain;
40  import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
41  import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
42  import org.apache.hc.core5.http.impl.Http1StreamListener;
43  import org.apache.hc.core5.http.impl.HttpProcessors;
44  import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory;
45  import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory;
46  import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandlerFactory;
47  import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
48  import org.apache.hc.core5.http.impl.routing.RequestRouter;
49  import org.apache.hc.core5.http.nio.AsyncFilterHandler;
50  import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
51  import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
52  import org.apache.hc.core5.http.nio.HandlerFactory;
53  import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
54  import org.apache.hc.core5.http.nio.support.AsyncServerExpectationFilter;
55  import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainElement;
56  import org.apache.hc.core5.http.nio.support.AsyncServerFilterChainExchangeHandlerFactory;
57  import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
58  import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
59  import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
60  import org.apache.hc.core5.http.nio.support.TerminalAsyncServerFilter;
61  import org.apache.hc.core5.http.protocol.HttpProcessor;
62  import org.apache.hc.core5.http.protocol.UriPatternType;
63  import org.apache.hc.core5.net.InetAddressUtils;
64  import org.apache.hc.core5.net.URIAuthority;
65  import org.apache.hc.core5.reactor.IOEventHandlerFactory;
66  import org.apache.hc.core5.reactor.IOReactorConfig;
67  import org.apache.hc.core5.reactor.IOSession;
68  import org.apache.hc.core5.reactor.IOSessionListener;
69  import org.apache.hc.core5.util.Args;
70  import org.apache.hc.core5.util.Timeout;
71  
72  /**
73   * {@link HttpAsyncServer} bootstrap.
74   *
75   * @since 5.0
76   */
77  @SuppressWarnings("deprecation")
78  public class AsyncServerBootstrap {
79  
80      private final List<RequestRouter.Entry<Supplier<AsyncServerExchangeHandler>>> routeEntries;
81      private final List<FilterEntry<AsyncFilterHandler>> filters;
82      private String canonicalHostName;
83      private HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> requestRouter;
84      private org.apache.hc.core5.http.protocol.LookupRegistry<Supplier<AsyncServerExchangeHandler>> lookupRegistry;
85      private IOReactorConfig ioReactorConfig;
86      private Http1Config http1Config;
87      private CharCodingConfig charCodingConfig;
88      private HttpProcessor httpProcessor;
89      private ConnectionReuseStrategy connStrategy;
90      private TlsStrategy tlsStrategy;
91      private Timeout handshakeTimeout;
92      private Decorator<IOSession> ioSessionDecorator;
93      private Callback<Exception> exceptionCallback;
94      private IOSessionListener sessionListener;
95      private Http1StreamListener streamListener;
96  
97      private AsyncServerBootstrap() {
98          this.routeEntries = new ArrayList<>();
99          this.filters = new ArrayList<>();
100     }
101 
102     public static AsyncServerBootstrap bootstrap() {
103         return new AsyncServerBootstrap();
104     }
105 
106     /**
107      * Sets canonical name (fully qualified domain name) of the server.
108      */
109     public final AsyncServerBootstrap setCanonicalHostName(final String canonicalHostName) {
110         this.canonicalHostName = canonicalHostName;
111         return this;
112     }
113 
114     /**
115      * Sets I/O reactor configuration.
116      */
117     public final AsyncServerBootstrap setIOReactorConfig(final IOReactorConfig ioReactorConfig) {
118         this.ioReactorConfig = ioReactorConfig;
119         return this;
120     }
121 
122     /**
123      * Sets HTTP/1.1 protocol parameters.
124      */
125     public final AsyncServerBootstrap setHttp1Config(final Http1Config http1Config) {
126         this.http1Config = http1Config;
127         return this;
128     }
129 
130     /**
131      * Sets connection configuration.
132      */
133     public final AsyncServerBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) {
134         this.charCodingConfig = charCodingConfig;
135         return this;
136     }
137 
138     /**
139      * Assigns {@link org.apache.hc.core5.http.protocol.HttpProcessor} instance.
140      */
141     public final AsyncServerBootstrap setHttpProcessor(final HttpProcessor httpProcessor) {
142         this.httpProcessor = httpProcessor;
143         return this;
144     }
145 
146     /**
147      * Assigns {@link org.apache.hc.core5.http.ConnectionReuseStrategy} instance.
148      */
149     public final AsyncServerBootstrap setConnectionReuseStrategy(final ConnectionReuseStrategy connStrategy) {
150         this.connStrategy = connStrategy;
151         return this;
152     }
153 
154     /**
155      * Assigns {@link TlsStrategy} instance.
156      */
157     public final AsyncServerBootstrap setTlsStrategy(final TlsStrategy tlsStrategy) {
158         this.tlsStrategy = tlsStrategy;
159         return this;
160     }
161 
162     /**
163      * Assigns TLS handshake {@link Timeout}.
164      */
165     public final AsyncServerBootstrap setTlsHandshakeTimeout(final Timeout handshakeTimeout) {
166         this.handshakeTimeout = handshakeTimeout;
167         return this;
168     }
169 
170     /**
171      * Assigns {@link IOSession} {@link Decorator} instance.
172      */
173     public final AsyncServerBootstrap setIOSessionDecorator(final Decorator<IOSession> ioSessionDecorator) {
174         this.ioSessionDecorator = ioSessionDecorator;
175         return this;
176     }
177 
178     /**
179      * Assigns {@link Exception} {@link Callback} instance.
180      */
181     public final AsyncServerBootstrap setExceptionCallback(final Callback<Exception> exceptionCallback) {
182         this.exceptionCallback = exceptionCallback;
183         return this;
184     }
185 
186     /**
187      * Assigns {@link IOSessionListener} instance.
188      */
189     public final AsyncServerBootstrap setIOSessionListener(final IOSessionListener sessionListener) {
190         this.sessionListener = sessionListener;
191         return this;
192     }
193 
194     /**
195      * @deprecated Use {@link RequestRouter}.
196      */
197     @Deprecated
198     public final AsyncServerBootstrap setLookupRegistry(final org.apache.hc.core5.http.protocol.LookupRegistry<Supplier<AsyncServerExchangeHandler>> lookupRegistry) {
199         this.lookupRegistry = lookupRegistry;
200         return this;
201     }
202 
203     /**
204      * Assigns {@link HttpRequestMapper} instance.
205      *
206      * @see org.apache.hc.core5.http.impl.routing.RequestRouter
207      * @since 5.3
208      */
209     public final AsyncServerBootstrap setRequestRouter(final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> requestRouter) {
210         this.requestRouter = requestRouter;
211         return this;
212     }
213 
214     /**
215      * Assigns {@link Http1StreamListener} instance.
216      *
217      * @since 5.0
218      */
219     public final AsyncServerBootstrap setStreamListener(final Http1StreamListener streamListener) {
220         this.streamListener = streamListener;
221         return this;
222     }
223 
224     /**
225      * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a default handler for URIs
226      * matching the given pattern.
227      *
228      * @param uriPattern the pattern to register the handler for.
229      * @param supplier the handler supplier.
230      */
231     public final AsyncServerBootstrap register(final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
232         Args.notBlank(uriPattern, "URI pattern");
233         Args.notNull(supplier, "Exchange handler supplier");
234         routeEntries.add(new RequestRouter.Entry<>(uriPattern, supplier));
235         return this;
236     }
237 
238     /**
239      * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a handler for URIs
240      * matching the given host and the pattern.
241      *
242      * @param hostname the host name
243      * @param uriPattern the pattern to register the handler for.
244      * @param supplier the handler supplier.
245      *
246      * @since 5.3
247      */
248     public final AsyncServerBootstrap register(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
249         Args.notBlank(hostname, "Hostname");
250         Args.notBlank(uriPattern, "URI pattern");
251         Args.notNull(supplier, "Exchange handler supplier");
252         routeEntries.add(new RequestRouter.Entry<>(hostname, uriPattern, supplier));
253         return this;
254     }
255 
256     /**
257      * @deprecated use {@link #register(String, String, Supplier)}.
258      */
259     @Deprecated
260     public final AsyncServerBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
261         return register(hostname, uriPattern, supplier);
262     }
263 
264     /**
265      * Registers the given {@link AsyncServerRequestHandler} as a default handler for URIs
266      * matching the given pattern.
267      *
268      * @param uriPattern the pattern to register the handler for.
269      * @param requestHandler the handler.
270      */
271     public final <T> AsyncServerBootstrap register(
272             final String uriPattern,
273             final AsyncServerRequestHandler<T> requestHandler) {
274         register(uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler));
275         return this;
276     }
277 
278     /**
279      * Registers the given {@link AsyncServerRequestHandler} as a handler for URIs
280      * matching the given host and the pattern.
281      *
282      * @param hostname the host name
283      * @param uriPattern the pattern to register the handler for.
284      * @param requestHandler the handler.
285      *
286      * @since 5.3
287      */
288     public final <T> AsyncServerBootstrap register(final String hostname, final String uriPattern, final AsyncServerRequestHandler<T> requestHandler) {
289         register(hostname, uriPattern, () -> new BasicServerExchangeHandler<>(requestHandler));
290         return this;
291     }
292 
293     /**
294      * @deprecated Use {@link #register(String, String, AsyncServerRequestHandler)}.
295      */
296     @Deprecated
297     public final <T> AsyncServerBootstrap registerVirtual(
298             final String hostname,
299             final String uriPattern,
300             final AsyncServerRequestHandler<T> requestHandler) {
301         return register(hostname, uriPattern, requestHandler);
302     }
303 
304     /**
305      * Adds the filter before the filter with the given name.
306      */
307     public final AsyncServerBootstrap addFilterBefore(final String existing, final String name, final AsyncFilterHandler filterHandler) {
308         Args.notBlank(existing, "Existing");
309         Args.notBlank(name, "Name");
310         Args.notNull(filterHandler, "Filter handler");
311         filters.add(new FilterEntry<>(FilterEntry.Position.BEFORE, name, filterHandler, existing));
312         return this;
313     }
314 
315     /**
316      * Adds the filter after the filter with the given name.
317      */
318     public final AsyncServerBootstrap addFilterAfter(final String existing, final String name, final AsyncFilterHandler filterHandler) {
319         Args.notBlank(existing, "Existing");
320         Args.notBlank(name, "Name");
321         Args.notNull(filterHandler, "Filter handler");
322         filters.add(new FilterEntry<>(FilterEntry.Position.AFTER, name, filterHandler, existing));
323         return this;
324     }
325 
326     /**
327      * Replace an existing filter with the given name with new filter.
328      */
329     public final AsyncServerBootstrap replaceFilter(final String existing, final AsyncFilterHandler filterHandler) {
330         Args.notBlank(existing, "Existing");
331         Args.notNull(filterHandler, "Filter handler");
332         filters.add(new FilterEntry<>(FilterEntry.Position.REPLACE, existing, filterHandler, existing));
333         return this;
334     }
335 
336     /**
337      * Add an filter to the head of the processing list.
338      */
339     public final AsyncServerBootstrap addFilterFirst(final String name, final AsyncFilterHandler filterHandler) {
340         Args.notNull(name, "Name");
341         Args.notNull(filterHandler, "Filter handler");
342         filters.add(new FilterEntry<>(FilterEntry.Position.FIRST, name, filterHandler, null));
343         return this;
344     }
345 
346     /**
347      * Add an filter to the tail of the processing list.
348      */
349     public final AsyncServerBootstrap addFilterLast(final String name, final AsyncFilterHandler filterHandler) {
350         Args.notNull(name, "Name");
351         Args.notNull(filterHandler, "Filter handler");
352         filters.add(new FilterEntry<>(FilterEntry.Position.LAST, name, filterHandler, null));
353         return this;
354     }
355 
356     public HttpAsyncServer create() {
357         final String actualCanonicalHostName = canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName();
358         final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> requestRouterCopy;
359         if (lookupRegistry != null && requestRouter == null) {
360             final org.apache.hc.core5.http.protocol.RequestHandlerRegistry<Supplier<AsyncServerExchangeHandler>> handlerRegistry = new org.apache.hc.core5.http.protocol.RequestHandlerRegistry<>(
361                     actualCanonicalHostName,
362                     () -> lookupRegistry != null ? lookupRegistry : new org.apache.hc.core5.http.protocol.UriPatternMatcher<>());
363             for (final RequestRouter.Entry<Supplier<AsyncServerExchangeHandler>> entry: routeEntries) {
364                 handlerRegistry.register(entry.uriAuthority != null ? entry.uriAuthority.getHostName() : null, entry.route.pattern, entry.route.handler);
365             }
366             requestRouterCopy = handlerRegistry;
367         } else {
368             if (routeEntries.isEmpty()) {
369                 requestRouterCopy = requestRouter;
370             } else {
371                 requestRouterCopy = RequestRouter.create(
372                         new URIAuthority(actualCanonicalHostName),
373                         UriPatternType.URI_PATTERN, routeEntries,
374                         RequestRouter.IGNORE_PORT_AUTHORITY_RESOLVER,
375                         requestRouter);
376             }
377         }
378         final HandlerFactory<AsyncServerExchangeHandler> handlerFactory;
379         if (!filters.isEmpty()) {
380             final NamedElementChain<AsyncFilterHandler> filterChainDefinition = new NamedElementChain<>();
381             filterChainDefinition.addLast(
382                     new TerminalAsyncServerFilter(new DefaultAsyncResponseExchangeHandlerFactory(requestRouterCopy)),
383                     StandardFilter.MAIN_HANDLER.name());
384             filterChainDefinition.addFirst(
385                     new AsyncServerExpectationFilter(),
386                     StandardFilter.EXPECT_CONTINUE.name());
387 
388             for (final FilterEntry<AsyncFilterHandler> entry: filters) {
389                 switch (entry.position) {
390                     case AFTER:
391                         filterChainDefinition.addAfter(entry.existing, entry.filterHandler, entry.name);
392                         break;
393                     case BEFORE:
394                         filterChainDefinition.addBefore(entry.existing, entry.filterHandler, entry.name);
395                         break;
396                     case REPLACE:
397                         filterChainDefinition.replace(entry.existing, entry.filterHandler);
398                         break;
399                     case FIRST:
400                         filterChainDefinition.addFirst(entry.filterHandler, entry.name);
401                         break;
402                     case LAST:
403                         // Don't add last, after TerminalAsyncServerFilter, as that does not delegate to the chain
404                         // Instead, add the filter just before it, making it effectively the last filter
405                         filterChainDefinition.addBefore(StandardFilter.MAIN_HANDLER.name(), entry.filterHandler, entry.name);
406                         break;
407                 }
408             }
409 
410             NamedElementChain<AsyncFilterHandler>.Node current = filterChainDefinition.getLast();
411             AsyncServerFilterChainElement execChain = null;
412             while (current != null) {
413                 execChain = new AsyncServerFilterChainElement(current.getValue(), execChain);
414                 current = current.getPrevious();
415             }
416 
417             handlerFactory = new AsyncServerFilterChainExchangeHandlerFactory(execChain, exceptionCallback);
418         } else {
419             handlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(requestRouterCopy, handler -> new BasicAsyncServerExpectationDecorator(handler, exceptionCallback));
420         }
421 
422         final ServerHttp1StreamDuplexerFactory streamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
423                 httpProcessor != null ? httpProcessor : HttpProcessors.server(),
424                 handlerFactory,
425                 http1Config,
426                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
427                 connStrategy != null ? connStrategy : DefaultConnectionReuseStrategy.INSTANCE,
428                 new DefaultHttpRequestParserFactory(http1Config),
429                 new DefaultHttpResponseWriterFactory(http1Config),
430                 DefaultContentLengthStrategy.INSTANCE,
431                 DefaultContentLengthStrategy.INSTANCE,
432                 streamListener);
433         final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttp1IOEventHandlerFactory(
434                 streamHandlerFactory,
435                 tlsStrategy,
436                 handshakeTimeout);
437         return new HttpAsyncServer(ioEventHandlerFactory, ioReactorConfig, ioSessionDecorator, exceptionCallback,
438                 sessionListener);
439     }
440 
441 }