1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.http2.examples;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.net.InetSocketAddress;
32 import java.net.URI;
33 import java.net.URISyntaxException;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.concurrent.Future;
37 import java.util.concurrent.TimeUnit;
38
39 import org.apache.hc.core5.http.ContentType;
40 import org.apache.hc.core5.http.EndpointDetails;
41 import org.apache.hc.core5.http.EntityDetails;
42 import org.apache.hc.core5.http.Header;
43 import org.apache.hc.core5.http.HttpConnection;
44 import org.apache.hc.core5.http.HttpException;
45 import org.apache.hc.core5.http.HttpRequest;
46 import org.apache.hc.core5.http.HttpStatus;
47 import org.apache.hc.core5.http.Message;
48 import org.apache.hc.core5.http.ProtocolException;
49 import org.apache.hc.core5.http.URIScheme;
50 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
51 import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
52 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
53 import org.apache.hc.core5.http.nio.entity.AsyncEntityProducers;
54 import org.apache.hc.core5.http.nio.entity.NoopEntityConsumer;
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.http.protocol.HttpCoreContext;
59 import org.apache.hc.core5.http2.HttpVersionPolicy;
60 import org.apache.hc.core5.http2.frame.RawFrame;
61 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
62 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
63 import org.apache.hc.core5.io.CloseMode;
64 import org.apache.hc.core5.reactor.IOReactorConfig;
65 import org.apache.hc.core5.reactor.ListenerEndpoint;
66 import org.apache.hc.core5.util.TimeValue;
67
68
69
70
71 public class H2FileServerExample {
72
73 public static void main(final String[] args) throws Exception {
74 if (args.length < 1) {
75 System.err.println("Please specify document root directory");
76 System.exit(1);
77 }
78
79 final File docRoot = new File(args[0]);
80 int port = 8080;
81 if (args.length >= 2) {
82 port = Integer.parseInt(args[1]);
83 }
84
85 final IOReactorConfig config = IOReactorConfig.custom()
86 .setSoTimeout(15, TimeUnit.SECONDS)
87 .setTcpNoDelay(true)
88 .build();
89
90 final HttpAsyncServer server = H2ServerBootstrap.bootstrap()
91 .setIOReactorConfig(config)
92 .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
93 .setStreamListener(new H2StreamListener() {
94
95 @Override
96 public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
97 for (int i = 0; i < headers.size(); i++) {
98 System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i));
99 }
100 }
101
102 @Override
103 public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
104 for (int i = 0; i < headers.size(); i++) {
105 System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i));
106 }
107 }
108
109 @Override
110 public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
111 }
112
113 @Override
114 public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
115 }
116
117 @Override
118 public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
119 }
120
121 @Override
122 public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
123 }
124
125 })
126 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, Void>>() {
127
128 @Override
129 public AsyncRequestConsumer<Message<HttpRequest, Void>> prepare(
130 final HttpRequest request,
131 final EntityDetails entityDetails,
132 final HttpContext context) throws HttpException {
133 return new BasicRequestConsumer<>(entityDetails != null ? new NoopEntityConsumer() : null);
134 }
135
136 @Override
137 public void handle(
138 final Message<HttpRequest, Void> message,
139 final ResponseTrigger responseTrigger,
140 final HttpContext context) throws HttpException, IOException {
141 final HttpRequest request = message.getHead();
142 final URI requestUri;
143 try {
144 requestUri = request.getUri();
145 } catch (final URISyntaxException ex) {
146 throw new ProtocolException(ex.getMessage(), ex);
147 }
148 final String path = requestUri.getPath();
149 final File file = new File(docRoot, path);
150 if (!file.exists()) {
151
152 System.out.println("File " + file.getPath() + " not found");
153 responseTrigger.submitResponse(
154 AsyncResponseBuilder.create(HttpStatus.SC_NOT_FOUND)
155 .setEntity("<html><body><h1>File" + file.getPath() +
156 " not found</h1></body></html>", ContentType.TEXT_HTML)
157 .build(),
158 context);
159
160 } else if (!file.canRead() || file.isDirectory()) {
161
162 System.out.println("Cannot read file " + file.getPath());
163 responseTrigger.submitResponse(
164 AsyncResponseBuilder.create(HttpStatus.SC_FORBIDDEN)
165 .setEntity("<html><body><h1>Access denied</h1></body></html>", ContentType.TEXT_HTML)
166 .build(),
167 context);
168
169 } else {
170
171 final ContentType contentType;
172 final String filename = file.getName().toLowerCase(Locale.ROOT);
173 if (filename.endsWith(".txt")) {
174 contentType = ContentType.TEXT_PLAIN;
175 } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
176 contentType = ContentType.TEXT_HTML;
177 } else if (filename.endsWith(".xml")) {
178 contentType = ContentType.TEXT_XML;
179 } else {
180 contentType = ContentType.DEFAULT_BINARY;
181 }
182
183 final HttpCoreContext coreContext = HttpCoreContext.adapt(context);
184 final EndpointDetails endpoint = coreContext.getEndpointDetails();
185 System.out.println(endpoint + ": serving file " + file.getPath());
186 responseTrigger.submitResponse(
187 AsyncResponseBuilder.create(HttpStatus.SC_OK)
188 .setEntity(AsyncEntityProducers.create(file, contentType))
189 .build(),
190 context);
191 }
192 }
193
194 })
195 .create();
196
197 Runtime.getRuntime().addShutdownHook(new Thread() {
198 @Override
199 public void run() {
200 System.out.println("HTTP server shutting down");
201 server.close(CloseMode.GRACEFUL);
202 }
203 });
204
205 server.start();
206 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
207 final ListenerEndpoint listenerEndpoint = future.get();
208 System.out.print("Listening on " + listenerEndpoint.getAddress());
209 server.awaitShutdown(TimeValue.ofDays(Long.MAX_VALUE));
210 }
211
212 }