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