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