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