blob: e4fc0ccd0000c0be06676c2b5c34cfffe5786c07 [file] [log] [blame]
T Jake Luciani322caa22010-02-15 03:24:55 +00001/*
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
28package test;
29
30import java.io.File;
31import java.io.IOException;
32import java.io.InterruptedIOException;
33import java.io.OutputStream;
34import java.io.OutputStreamWriter;
35import java.net.ServerSocket;
36import java.net.Socket;
37import java.net.URLDecoder;
38import java.util.Locale;
39
40import org.apache.http.ConnectionClosedException;
41import org.apache.http.HttpEntity;
42import org.apache.http.HttpEntityEnclosingRequest;
43import org.apache.http.HttpException;
44import org.apache.http.HttpRequest;
45import org.apache.http.HttpResponse;
46import org.apache.http.HttpServerConnection;
47import org.apache.http.HttpStatus;
48import org.apache.http.MethodNotSupportedException;
49import org.apache.http.entity.ContentProducer;
50import org.apache.http.entity.EntityTemplate;
51import org.apache.http.entity.FileEntity;
52import org.apache.http.impl.DefaultHttpResponseFactory;
53import org.apache.http.impl.DefaultHttpServerConnection;
54import org.apache.http.impl.NoConnectionReuseStrategy;
55import org.apache.http.params.BasicHttpParams;
56import org.apache.http.params.CoreConnectionPNames;
57import org.apache.http.params.CoreProtocolPNames;
58import org.apache.http.params.HttpParams;
59import org.apache.http.protocol.BasicHttpContext;
60import org.apache.http.protocol.BasicHttpProcessor;
61import org.apache.http.protocol.HttpContext;
62import org.apache.http.protocol.HttpProcessor;
63import org.apache.http.protocol.HttpRequestHandler;
64import org.apache.http.protocol.HttpRequestHandlerRegistry;
65import org.apache.http.protocol.HttpService;
66import org.apache.http.util.EntityUtils;
67import org.apache.thrift.TProcessor;
68import org.apache.thrift.protocol.TJSONProtocol;
69import org.apache.thrift.protocol.TProtocol;
70import org.apache.thrift.transport.TMemoryBuffer;
71
72import thrift.test.ThriftTest;
Roger Meier37b5bf82010-10-24 21:41:24 +000073import org.apache.thrift.server.ServerTestBase.TestHandler;
T Jake Luciani322caa22010-02-15 03:24:55 +000074
Roger Meierc8d5d4d2012-01-07 20:32:24 +000075import eu.medsea.mimeutil.detector.ExtensionMimeDetector;
76import eu.medsea.mimeutil.MimeUtil2;
77import eu.medsea.mimeutil.MimeType;
78import java.util.Collection;
79import java.util.Iterator;
80
T Jake Luciani322caa22010-02-15 03:24:55 +000081/**
82 * Basic, yet fully functional and spec compliant, HTTP/1.1 file server.
83 * <p>
84 * Please note the purpose of this application is demonstrate the usage of
85 * HttpCore APIs. It is NOT intended to demonstrate the most efficient way of
86 * building an HTTP file server.
87 *
88 *
89 */
90public class Httpd {
91
92 public static void main(String[] args) throws Exception {
93 if (args.length < 1) {
94 System.err.println("Please specify document root directory");
95 System.exit(1);
96 }
97 Thread t = new RequestListenerThread(8088, args[0]);
98 t.setDaemon(false);
99 t.start();
100 }
101
102 static class HttpFileHandler implements HttpRequestHandler {
103
104 private final String docRoot;
105
106 public HttpFileHandler(final String docRoot) {
107 super();
108 this.docRoot = docRoot;
109 }
110
111 public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException {
112
113 String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH);
114 if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) {
115 throw new MethodNotSupportedException(method + " method not supported");
116 }
117 String target = request.getRequestLine().getUri();
118
119 if (request instanceof HttpEntityEnclosingRequest && target.equals("/service")) {
120 HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
121 byte[] entityContent = EntityUtils.toByteArray(entity);
122 System.out.println("Incoming content: " + new String(entityContent));
123
124 final String output = this.thriftRequest(entityContent);
125
126 System.out.println("Outgoing content: "+output);
127
128 EntityTemplate body = new EntityTemplate(new ContentProducer() {
129
130 public void writeTo(final OutputStream outstream) throws IOException {
131 OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
132 writer.write(output);
133 writer.flush();
134 }
135
136 });
137 body.setContentType("text/html; charset=UTF-8");
138 response.setEntity(body);
139 } else {
Roger Meier0b267252011-07-29 21:08:04 +0000140 if(target.indexOf("?") != -1) {
141 target = target.substring(1, target.indexOf("?"));
142 }
T Jake Luciani322caa22010-02-15 03:24:55 +0000143
Roger Meier0b267252011-07-29 21:08:04 +0000144 final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
145
T Jake Luciani322caa22010-02-15 03:24:55 +0000146 if (!file.exists()) {
147
148 response.setStatusCode(HttpStatus.SC_NOT_FOUND);
149 EntityTemplate body = new EntityTemplate(new ContentProducer() {
150
151 public void writeTo(final OutputStream outstream) throws IOException {
152 OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
153 writer.write("<html><body><h1>");
154 writer.write("File ");
155 writer.write(file.getPath());
156 writer.write(" not found");
157 writer.write("</h1></body></html>");
158 writer.flush();
159 }
160
161 });
162 body.setContentType("text/html; charset=UTF-8");
163 response.setEntity(body);
164 System.out.println("File " + file.getPath() + " not found");
165
166 } else if (!file.canRead() || file.isDirectory()) {
167
168 response.setStatusCode(HttpStatus.SC_FORBIDDEN);
169 EntityTemplate body = new EntityTemplate(new ContentProducer() {
170
171 public void writeTo(final OutputStream outstream) throws IOException {
172 OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
173 writer.write("<html><body><h1>");
174 writer.write("Access denied");
175 writer.write("</h1></body></html>");
176 writer.flush();
177 }
178
179 });
180 body.setContentType("text/html; charset=UTF-8");
181 response.setEntity(body);
182 System.out.println("Cannot read file " + file.getPath());
183
184 } else {
185
Nobuaki Sukegawafbc69772015-05-10 23:34:19 +0900186 String mimeType = "application/octet-stream";
187 MimeUtil2 mimeUtil = new MimeUtil2();
188 synchronized (this) {
189 mimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName());
190 }
191 Collection<MimeType> collection = mimeUtil.getMimeTypes(file);
192 Iterator<MimeType> iterator = collection.iterator();
193 while(iterator.hasNext()) {
194 MimeType mt = iterator.next();
195 mimeType = mt.getMediaType() + "/" + mt.getSubType();
196 break;
197 }
Roger Meierc8d5d4d2012-01-07 20:32:24 +0000198
T Jake Luciani322caa22010-02-15 03:24:55 +0000199 response.setStatusCode(HttpStatus.SC_OK);
Roger Meierc8d5d4d2012-01-07 20:32:24 +0000200 FileEntity body = new FileEntity(file, mimeType);
201 response.addHeader("Content-Type", mimeType);
T Jake Luciani322caa22010-02-15 03:24:55 +0000202 response.setEntity(body);
203 System.out.println("Serving file " + file.getPath());
204
205 }
206 }
207 }
208
209 private String thriftRequest(byte[] input){
210 try{
211
212 //Input
213 TMemoryBuffer inbuffer = new TMemoryBuffer(input.length);
214 inbuffer.write(input);
215 TProtocol inprotocol = new TJSONProtocol(inbuffer);
216
217 //Output
218 TMemoryBuffer outbuffer = new TMemoryBuffer(100);
219 TProtocol outprotocol = new TJSONProtocol(outbuffer);
220
221 TProcessor processor = new ThriftTest.Processor(new TestHandler());
222 processor.process(inprotocol, outprotocol);
223
224 byte[] output = new byte[outbuffer.length()];
225 outbuffer.readAll(output, 0, output.length);
226
227 return new String(output,"UTF-8");
228 }catch(Throwable t){
229 return "Error:"+t.getMessage();
230 }
231
232
233 }
234
235 }
236
237 static class RequestListenerThread extends Thread {
238
239 private final ServerSocket serversocket;
240 private final HttpParams params;
241 private final HttpService httpService;
242
243 public RequestListenerThread(int port, final String docroot) throws IOException {
244 this.serversocket = new ServerSocket(port);
245 this.params = new BasicHttpParams();
246 this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
247 .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
248 .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
249
250 // Set up the HTTP protocol processor
251 HttpProcessor httpproc = new BasicHttpProcessor();
252
253 // Set up request handlers
254 HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
255 reqistry.register("*", new HttpFileHandler(docroot));
256
257 // Set up the HTTP service
258 this.httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory());
259 this.httpService.setParams(this.params);
260 this.httpService.setHandlerResolver(reqistry);
261 }
262
263 public void run() {
264 System.out.println("Listening on port " + this.serversocket.getLocalPort());
265 System.out.println("Point your browser to http://localhost:8088/test/test.html");
266
267 while (!Thread.interrupted()) {
268 try {
269 // Set up HTTP connection
270 Socket socket = this.serversocket.accept();
271 DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
272 System.out.println("Incoming connection from " + socket.getInetAddress());
273 conn.bind(socket, this.params);
274
275 // Start worker thread
276 Thread t = new WorkerThread(this.httpService, conn);
277 t.setDaemon(true);
278 t.start();
279 } catch (InterruptedIOException ex) {
280 break;
281 } catch (IOException e) {
282 System.err.println("I/O error initialising connection thread: " + e.getMessage());
283 break;
284 }
285 }
286 }
287 }
288
289 static class WorkerThread extends Thread {
290
291 private final HttpService httpservice;
292 private final HttpServerConnection conn;
293
294 public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) {
295 super();
296 this.httpservice = httpservice;
297 this.conn = conn;
298 }
299
300 public void run() {
301 System.out.println("New connection thread");
302 HttpContext context = new BasicHttpContext(null);
303 try {
304 while (!Thread.interrupted() && this.conn.isOpen()) {
305 this.httpservice.handleRequest(this.conn, context);
306 }
307 } catch (ConnectionClosedException ex) {
308 System.err.println("Client closed connection");
309 } catch (IOException ex) {
310 System.err.println("I/O error: " + ex.getMessage());
311 } catch (HttpException ex) {
312 System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
313 } finally {
314 try {
315 this.conn.shutdown();
316 } catch (IOException ignore) {
317 }
318 }
319 }
320
321 }
322
323}