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 .setIOReactorConfig(config)
90 .register("*", new AsyncServerRequestHandler<Message<HttpRequest, Void>>() {
91
92 @Override
93 public AsyncRequestConsumer<Message<HttpRequest, Void>> prepare(
94 final HttpRequest request,
95 final EntityDetails entityDetails,
96 final HttpContext context) throws HttpException {
97 return new BasicRequestConsumer<>(entityDetails != null ? new DiscardingEntityConsumer<>() : null);
98 }
99
100 @Override
101 public void handle(
102 final Message<HttpRequest, Void> message,
103 final ResponseTrigger responseTrigger,
104 final HttpContext context) throws HttpException, IOException {
105 final HttpRequest request = message.getHead();
106 final URI requestUri;
107 try {
108 requestUri = request.getUri();
109 } catch (final URISyntaxException ex) {
110 throw new ProtocolException(ex.getMessage(), ex);
111 }
112 final String path = requestUri.getPath();
113 final File file = new File(docRoot, path);
114 if (!file.exists()) {
115
116 final String msg = "File " + file.getPath() + " not found";
117 println(msg);
118 responseTrigger.submitResponse(
119 AsyncResponseBuilder.create(HttpStatus.SC_NOT_FOUND)
120 .setEntity("<html><body><h1>" + msg + "</h1></body></html>", ContentType.TEXT_HTML)
121 .build(),
122 context);
123
124 } else if (!file.canRead() || file.isDirectory()) {
125
126 final String msg = "Cannot read file " + file.getPath();
127 println(msg);
128 responseTrigger.submitResponse(AsyncResponseBuilder.create(HttpStatus.SC_FORBIDDEN)
129 .setEntity("<html><body><h1>" + msg + "</h1></body></html>", ContentType.TEXT_HTML)
130 .build(),
131 context);
132
133 } else {
134
135 final ContentType contentType;
136 final String filename = TextUtils.toLowerCase(file.getName());
137 if (filename.endsWith(".txt")) {
138 contentType = ContentType.TEXT_PLAIN;
139 } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
140 contentType = ContentType.TEXT_HTML;
141 } else if (filename.endsWith(".xml")) {
142 contentType = ContentType.TEXT_XML;
143 } else {
144 contentType = ContentType.DEFAULT_BINARY;
145 }
146
147 final HttpCoreContext coreContext = HttpCoreContext.adapt(context);
148 final EndpointDetails endpoint = coreContext.getEndpointDetails();
149
150 println(endpoint + " | serving file " + file.getPath());
151
152 responseTrigger.submitResponse(
153 AsyncResponseBuilder.create(HttpStatus.SC_OK)
154 .setEntity(AsyncEntityProducers.create(file, contentType))
155 .build(),
156 context);
157 }
158 }
159
160 })
161 .create();
162
163 Runtime.getRuntime().addShutdownHook(new Thread(() -> {
164 println("HTTP server shutting down");
165 server.close(CloseMode.GRACEFUL);
166 }));
167
168 server.start();
169 final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
170 final ListenerEndpoint listenerEndpoint = future.get();
171 println("Listening on " + listenerEndpoint.getAddress());
172 server.awaitShutdown(TimeValue.MAX_VALUE);
173 }
174
175 static void println(final String msg) {
176 System.out.println(HttpDateGenerator.INSTANCE.getCurrentDate() + " | " + msg);
177 }
178
179 }