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                  .setIOReactorConfig(config)
80  
81                  // Replace standard expect-continue handling with a custom auth filter
82  
83                  .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), new AbstractAsyncServerAuthFilter<String>(true) {
84  
85                      @Override
86                      protected String parseChallengeResponse(
87                              final String authorizationValue, final HttpContext context) throws HttpException {
88                          return authorizationValue;
89                      }
90  
91                      @Override
92                      protected boolean authenticate(
93                              final String challengeResponse,
94                              final URIAuthority authority,
95                              final String requestUri,
96                              final HttpContext context) {
97                          return "let me pass".equals(challengeResponse);
98                      }
99  
100                     @Override
101                     protected String generateChallenge(
102                             final String challengeResponse,
103                             final URIAuthority authority,
104                             final String requestUri,
105                             final HttpContext context) {
106                         return "who goes there?";
107                     }
108 
109                 })
110 
111                 // Add a custom request filter at the beginning of the processing pipeline
112 
113                 .addFilterFirst("my-filter", (request, entityDetails, context, responseTrigger, chain) -> {
114                     if (request.getRequestUri().equals("/back-door")) {
115                         responseTrigger.submitResponse(
116                                 new BasicHttpResponse(HttpStatus.SC_OK),
117                                 AsyncEntityProducers.create("Welcome"));
118                         return null;
119                     }
120                     return chain.proceed(request, entityDetails, context, new AsyncFilterChain.ResponseTrigger() {
121 
122                         @Override
123                         public void sendInformation(
124                                 final HttpResponse response) throws HttpException, IOException {
125                             responseTrigger.sendInformation(response);
126                         }
127 
128                         @Override
129                         public void submitResponse(
130                                 final HttpResponse response, final AsyncEntityProducer entityProducer) throws HttpException, IOException {
131                             response.addHeader("X-Filter", "My-Filter");
132                             responseTrigger.submitResponse(response, entityProducer);
133                         }
134 
135                         @Override
136                         public void pushPromise(
137                                 final HttpRequest promise, final AsyncPushProducer responseProducer) throws HttpException, IOException {
138                             responseTrigger.pushPromise(promise, responseProducer);
139                         }
140 
141                     });
142                 })
143 
144                 // Application request handler
145 
146                 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, String>>() {
147 
148                     @Override
149                     public AsyncRequestConsumer<Message<HttpRequest, String>> prepare(
150                             final HttpRequest request,
151                             final EntityDetails entityDetails,
152                             final HttpContext context) throws HttpException {
153                         return new BasicRequestConsumer<>(entityDetails != null ? new StringAsyncEntityConsumer() : null);
154                     }
155 
156                     @Override
157                     public void handle(
158                             final Message<HttpRequest, String> requestMessage,
159                             final ResponseTrigger responseTrigger,
160                             final HttpContext context) throws HttpException, IOException {
161                         // do something useful
162                         responseTrigger.submitResponse(
163                                 AsyncResponseBuilder.create(HttpStatus.SC_OK)
164                                         .setEntity("Hello")
165                                         .build(),
166                                 context);
167                     }
168                 })
169                 .create();
170 
171         Runtime.getRuntime().addShutdownHook(new Thread(() -> {
172             System.out.println("HTTP server shutting down");
173             server.close(CloseMode.GRACEFUL);
174         }));
175 
176         server.start();
177         final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
178         final ListenerEndpoint listenerEndpoint = future.get();
179         System.out.print("Listening on " + listenerEndpoint.getAddress());
180         server.awaitShutdown(TimeValue.MAX_VALUE);
181     }
182 
183 }