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.impl.bootstrap.AsyncServerBootstrap;
41  import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
42  import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
43  import org.apache.hc.core5.http.message.BasicHttpResponse;
44  import org.apache.hc.core5.http.nio.AsyncDataConsumer;
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.AsyncFilterHandler;
48  import org.apache.hc.core5.http.nio.AsyncPushProducer;
49  import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
50  import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
51  import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
52  import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
53  import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter;
54  import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder;
55  import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
56  import org.apache.hc.core5.http.protocol.HttpContext;
57  import org.apache.hc.core5.io.CloseMode;
58  import org.apache.hc.core5.net.URIAuthority;
59  import org.apache.hc.core5.reactor.IOReactorConfig;
60  import org.apache.hc.core5.reactor.ListenerEndpoint;
61  import org.apache.hc.core5.util.TimeValue;
62  
63  /**
64   * Example of using asynchronous I/O request filters with an embedded HTTP/1.1 server.
65   */
66  public class AsyncServerFilterExample {
67  
68      public static void main(final String[] args) throws Exception {
69          int port = 8080;
70          if (args.length >= 1) {
71              port = Integer.parseInt(args[0]);
72          }
73  
74          final IOReactorConfig config = IOReactorConfig.custom()
75                  .setSoTimeout(15, TimeUnit.SECONDS)
76                  .setTcpNoDelay(true)
77                  .build();
78  
79          final HttpAsyncServer server = AsyncServerBootstrap.bootstrap()
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", new AsyncFilterHandler() {
115 
116                     @Override
117                     public AsyncDataConsumer handle(
118                             final HttpRequest request,
119                             final EntityDetails entityDetails,
120                             final HttpContext context,
121                             final AsyncFilterChain.ResponseTrigger responseTrigger,
122                             final AsyncFilterChain chain) throws HttpException, IOException {
123                         if (request.getRequestUri().equals("/back-door")) {
124                             responseTrigger.submitResponse(
125                                     new BasicHttpResponse(HttpStatus.SC_OK),
126                                     AsyncEntityProducers.create("Welcome"));
127                             return null;
128                         }
129                         return chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() {
130 
131                             @Override
132                             public void sendInformation(
133                                     final HttpResponse response) throws HttpException, IOException {
134                                 responseTrigger.sendInformation(response);
135                             }
136 
137                             @Override
138                             public void submitResponse(
139                                     final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException {
140                                 response.addHeader("X-Filter", "My-Filter");
141                                 responseTrigger.submitResponse(response, entityProducer);
142                             }
143 
144                             @Override
145                             public void pushPromise(
146                                     final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException {
147                                 responseTrigger.pushPromise(promise, responseProducer);
148                             }
149 
150                         });
151                     }
152 
153                 })
154 
155                 // Application request handler
156 
157                 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, String>>() {
158 
159                     @Override
160                     public AsyncRequestConsumer<Message<HttpRequest, String>> prepare(
161                             final HttpRequest request,
162                             final EntityDetails entityDetails,
163                             final HttpContext context) throws HttpException {
164                         return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null);
165                     }
166 
167                     @Override
168                     public void handle(
169                             final Message<HttpRequest, String> requestMessage,
170                             final ResponseTrigger responseTrigger,
171                             final HttpContext context) throws HttpException, IOException {
172                         // do something useful
173                         responseTrigger.submitResponse(
174                                 AsyncResponseBuilder.create(HttpStatus.SC_OK)
175                                         .setEntity("Hello")
176                                         .build(),
177                                 context);
178                     }
179                 })
180                 .create();
181 
182         Runtime.getRuntime().addShutdownHook(new Thread() {
183             @Override
184             public void run() {
185                 System.out.println("HTTP server shutting down");
186                 server.close(CloseMode.GRACEFUL);
187             }
188         });
189 
190         server.start();
191         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port));
192         final ListenerEndpoint listenerEndpoint = future.get();
193         System.out.print("Listening on " + listenerEndpoint.getAddress());
194         server.awaitShutdown(TimeValue.MAX_VALUE);
195     }
196 
197 }