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.examples;
28  
29  import java.io.IOException;
30  import java.net.InetSocketAddress;
31  import java.util.concurrent.Future;
32  import java.util.concurrent.TimeUnit;
33  
34  import org.apache.hc.core5.http.EntityDetails;
35  import org.apache.hc.core5.http.HttpException;
36  import org.apache.hc.core5.http.HttpRequest;
37  import org.apache.hc.core5.http.HttpResponse;
38  import org.apache.hc.core5.http.HttpStatus;
39  import org.apache.hc.core5.http.Message;
40  import org.apache.hc.core5.http.URIScheme;
41  import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap;
42  import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
43  import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
44  import org.apache.hc.core5.http.message.BasicHttpResponse;
45  import org.apache.hc.core5.http.nio.AsyncEntityProducer;
46  import org.apache.hc.core5.http.nio.AsyncFilterChain;
47  import org.apache.hc.core5.http.nio.AsyncPushProducer;
48  import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
49  import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
50  import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
51  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
52  import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter;
53  import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder;
54  import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
55  import org.apache.hc.core5.http.protocol.HttpContext;
56  import org.apache.hc.core5.io.CloseMode;
57  import org.apache.hc.core5.net.URIAuthority;
58  import org.apache.hc.core5.reactor.IOReactorConfig;
59  import org.apache.hc.core5.reactor.ListenerEndpoint;
60  import org.apache.hc.core5.util.TimeValue;
61  
62  /**
63   * Example of using asynchronous I/O request filters with an embedded HTTP/1.1 server.
64   */
65  public class AsyncServerFilterExample {
66  
67      public static void main(final String[] args) throws Exception {
68          int port = 8080;
69          if (args.length >= 1) {
70              port = Integer.parseInt(args[0]);
71          }
72  
73          final IOReactorConfig config = IOReactorConfig.custom()
74                  .setSoTimeout(15, TimeUnit.SECONDS)
75                  .setTcpNoDelay(true)
76                  .build();
77  
78          final HttpAsyncServer server = AsyncServerBootstrap.bootstrap()
79                  .setExceptionCallback(e -> e.printStackTrace())
80                  .setIOReactorConfig(config)
81  
82                  // Replace standard expect-continue handling with a custom auth filter
83  
84                  .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter<String>(true) {
85  
86                      @Override
87                      protected String parseChallengeResponse(
88                              final String authorizationValue, final HttpContext context) throws HttpException {
89                          return authorizationValue;
90                      }
91  
92                      @Override
93                      protected boolean authenticate(
94                              final String challengeResponse,
95                              final URIAuthority authority,
96                              final String requestUri,
97                              final HttpContext context) {
98                          return "let me pass".equals(challengeResponse);
99                      }
100 
101                     @Override
102                     protected String generateChallenge(
103                             final String challengeResponse,
104                             final URIAuthority authority,
105                             final String requestUri,
106                             final HttpContext context) {
107                         return "who goes there?";
108                     }
109 
110                 })
111 
112                 // Add a custom request filter at the beginning of the processing pipeline
113 
114                 .addFilterFirst("my-filter", (request, entityDetails, context, responseTrigger, chain) -> {
115                     if (request.getRequestUri().equals("/back-door")) {
116                         responseTrigger.submitResponse(
117                                 new BasicHttpResponse(HttpStatus.SC_OK),
118                                 AsyncEntityProducers.create("Welcome"));
119                         return null;
120                     }
121                     return chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() {
122 
123                         @Override
124                         public void sendInformation(
125                                 final HttpResponse response) throws HttpException, IOException {
126                             responseTrigger.sendInformation(response);
127                         }
128 
129                         @Override
130                         public void submitResponse(
131                                 final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException {
132                             response.addHeader("X-Filter", "My-Filter");
133                             responseTrigger.submitResponse(response, entityProducer);
134                         }
135 
136                         @Override
137                         public void pushPromise(
138                                 final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException {
139                             responseTrigger.pushPromise(promise, responseProducer);
140                         }
141 
142                     });
143                 })
144 
145                 // Application request handler
146 
147                 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, String>>() {
148 
149                     @Override
150                     public AsyncRequestConsumer<Message<HttpRequest, String>> prepare(
151                             final HttpRequest request,
152                             final EntityDetails entityDetails,
153                             final HttpContext context) throws HttpException {
154                         return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null);
155                     }
156 
157                     @Override
158                     public void handle(
159                             final Message<HttpRequest, String> requestMessage,
160                             final ResponseTrigger responseTrigger,
161                             final HttpContext context) throws HttpException, IOException {
162                         // do something useful
163                         responseTrigger.submitResponse(
164                                 AsyncResponseBuilder.create(HttpStatus.SC_OK)
165                                         .setEntity("Hello")
166                                         .build(),
167                                 context);
168                     }
169                 })
170                 .create();
171 
172         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
173             System.out.println("HTTP server shutting down");
174             server.close(CloseMode.GRACEFUL);
175         }));
176 
177         server.start();
178         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
179         final ListenerEndpoint listenerEndpoint = future.get();
180         System.out.print("Listening on " + listenerEndpoint.getAddress());
181         server.awaitShutdown(TimeValue.MAX_VALUE);
182     }
183 
184 }