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.http.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.concurrent.Future;
35 import java.util.concurrent.TimeUnit;
36
37 import org.apache.hc.core5.http.ContentType;
38 import org.apache.hc.core5.http.EndpointDetails;
39 import org.apache.hc.core5.http.EntityDetails;
40 import org.apache.hc.core5.http.HttpException;
41 import org.apache.hc.core5.http.HttpRequest;
42 import org.apache.hc.core5.http.HttpStatus;
43 import org.apache.hc.core5.http.Message;
44 import org.apache.hc.core5.http.ProtocolException;
45 import org.apache.hc.core5.http.URIScheme;
46 import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap;
47 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
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.DiscardingEntityConsumer;
52 import org.apache.hc.core5.http.nio.support.AsyncResponseBuilder;
53 import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
54 import org.apache.hc.core5.http.protocol.HttpContext;
55 import org.apache.hc.core5.http.protocol.HttpCoreContext;
56 import org.apache.hc.core5.http.protocol.HttpDateGenerator;
57 import org.apache.hc.core5.io.CloseMode;
58 import org.apache.hc.core5.reactor.IOReactorConfig;
59 import org.apache.hc.core5.reactor.ListenerEndpoint;
60 import org.apache.hc.core5.util.TextUtils;
61 import org.apache.hc.core5.util.TimeValue;
62
63
64
65
66 public class AsyncFileServerExample {
67
68
69
70
71 public static void main(final String[] args) throws Exception {
72 if (args.length < 1) {
73 System.err.println("Please specify document root directory");
74 System.exit(1);
75 }
76
77 final File docRoot = new File(args[0]);
78 int port = 8080;
79 if (args.length >= 2) {
80 port = Integer.parseInt(args[1]);
81 }
82
83 final IOReactorConfig config = IOReactorConfig.custom()
84 .setSoTimeout(15, TimeUnit.SECONDS)
85 .setTcpNoDelay(true)
86 .build();
87
88 final HttpAsyncServer server = AsyncServerBootstrap.bootstrap()
89 .setExceptionCallback(e -> e.printStackTrace())
90 .setIOReactorConfig(config)
91 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, Void>>() {
92
93 @Override
94 public AsyncRequestConsumer<Message<HttpRequest, Void>> prepare(
95 final HttpRequest request,
96 final EntityDetails entityDetails,
97 final HttpContext context) throws HttpException {
98 return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null);
99 }
100
101 @Override
102 public void handle(
103 final Message<HttpRequest, Void> message,
104 final ResponseTrigger responseTrigger,
105 final HttpContext context) throws HttpException, IOException {
106 final HttpRequest request = message.getHead();
107 final URI requestUri;
108 try {
109 requestUri = request.getUri();
110 } catch (final URISyntaxException ex) {
111 throw new ProtocolException(ex.getMessage(), ex);
112 }
113 final String path = requestUri.getPath();
114 final File file = new File(docRoot, path);
115 if (!file.exists()) {
116
117 final String msg = "File " + file.getPath() + " not found";
118 println(msg);
119 responseTrigger.submitResponse(
120 AsyncResponseBuilder.create(HttpStatus.SC_NOT_FOUND)
121 .setEntity("<html><body><h1>" + msg + "</h1></body></html>", ContentType.TEXT_HTML)
122 .build(),
123 context);
124
125 } else if (!file.canRead() || file.isDirectory()) {
126
127 final String msg = "Cannot read file " + file.getPath();
128 println(msg);
129 responseTrigger.submitResponse(AsyncResponseBuilder.create(HttpStatus.SC_FORBIDDEN)
130 .setEntity("<html><body><h1>" + msg + "</h1></body></html>", ContentType.TEXT_HTML)
131 .build(),
132 context);
133
134 } else {
135
136 final ContentType contentType;
137 final String filename = TextUtils.toLowerCase(file.getName());
138 if (filename.endsWith(".txt")) {
139 contentType = ContentType.TEXT_PLAIN;
140 } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
141 contentType = ContentType.TEXT_HTML;
142 } else if (filename.endsWith(".xml")) {
143 contentType = ContentType.TEXT_XML;
144 } else {
145 contentType = ContentType.DEFAULT_BINARY;
146 }
147
148 final HttpCoreContext coreContext = HttpCoreContext.adapt(context);
149 final EndpointDetails endpoint = coreContext.getEndpointDetails();
150
151 println(endpoint + " | serving file " + file.getPath());
152
153 responseTrigger.submitResponse(
154 AsyncResponseBuilder.create(HttpStatus.SC_OK)
155 .setEntity(AsyncEntityProducers.create(file, contentType))
156 .build(),
157 context);
158 }
159 }
160
161 })
162 .create();
163
164 Runtime.getRuntime().addShutdownHook(new Thread(() -> {
165 println("HTTP server shutting down");
166 server.close(CloseMode.GRACEFUL);
167 }));
168
169 server.start();
170 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
171 final ListenerEndpoint listenerEndpoint = future.get();
172 println("Listening on " + listenerEndpoint.getAddress());
173 server.awaitShutdown(TimeValue.MAX_VALUE);
174 }
175
176 static void println(final String msg) {
177 System.out.println(HttpDateGenerator.INSTANCE.getCurrentDate() + " | " + msg);
178 }
179
180 }