blob: f1291d8018c92cffe2065ec28504d46518abc244 [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
Roger Meierc8d5d4d2012-01-07 20:32:24 +0000186 String mimeType = "application/octet-stream";
187 MimeUtil2 mimeUtil = new MimeUtil2();
188 mimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName());
189 Collection<MimeType> collection = mimeUtil.getMimeTypes(file);
190 Iterator<MimeType> iterator = collection.iterator();
191 while(iterator.hasNext()) {
192 MimeType mt = iterator.next();
193 mimeType = mt.getMediaType() + "/" + mt.getSubType();
194 break;
195 }
196
T Jake Luciani322caa22010-02-15 03:24:55 +0000197 response.setStatusCode(HttpStatus.SC_OK);
Roger Meierc8d5d4d2012-01-07 20:32:24 +0000198 FileEntity body = new FileEntity(file, mimeType);
199 response.addHeader("Content-Type", mimeType);
T Jake Luciani322caa22010-02-15 03:24:55 +0000200 response.setEntity(body);
201 System.out.println("Serving file " + file.getPath());
202
203 }
204 }
205 }
206
207 private String thriftRequest(byte[] input){
208 try{
209
210 //Input
211 TMemoryBuffer inbuffer = new TMemoryBuffer(input.length);
212 inbuffer.write(input);
213 TProtocol inprotocol = new TJSONProtocol(inbuffer);
214
215 //Output
216 TMemoryBuffer outbuffer = new TMemoryBuffer(100);
217 TProtocol outprotocol = new TJSONProtocol(outbuffer);
218
219 TProcessor processor = new ThriftTest.Processor(new TestHandler());
220 processor.process(inprotocol, outprotocol);
221
222 byte[] output = new byte[outbuffer.length()];
223 outbuffer.readAll(output, 0, output.length);
224
225 return new String(output,"UTF-8");
226 }catch(Throwable t){
227 return "Error:"+t.getMessage();
228 }
229
230
231 }
232
233 }
234
235 static class RequestListenerThread extends Thread {
236
237 private final ServerSocket serversocket;
238 private final HttpParams params;
239 private final HttpService httpService;
240
241 public RequestListenerThread(int port, final String docroot) throws IOException {
242 this.serversocket = new ServerSocket(port);
243 this.params = new BasicHttpParams();
244 this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
245 .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
246 .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
247
248 // Set up the HTTP protocol processor
249 HttpProcessor httpproc = new BasicHttpProcessor();
250
251 // Set up request handlers
252 HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry();
253 reqistry.register("*", new HttpFileHandler(docroot));
254
255 // Set up the HTTP service
256 this.httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory());
257 this.httpService.setParams(this.params);
258 this.httpService.setHandlerResolver(reqistry);
259 }
260
261 public void run() {
262 System.out.println("Listening on port " + this.serversocket.getLocalPort());
263 System.out.println("Point your browser to http://localhost:8088/test/test.html");
264
265 while (!Thread.interrupted()) {
266 try {
267 // Set up HTTP connection
268 Socket socket = this.serversocket.accept();
269 DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
270 System.out.println("Incoming connection from " + socket.getInetAddress());
271 conn.bind(socket, this.params);
272
273 // Start worker thread
274 Thread t = new WorkerThread(this.httpService, conn);
275 t.setDaemon(true);
276 t.start();
277 } catch (InterruptedIOException ex) {
278 break;
279 } catch (IOException e) {
280 System.err.println("I/O error initialising connection thread: " + e.getMessage());
281 break;
282 }
283 }
284 }
285 }
286
287 static class WorkerThread extends Thread {
288
289 private final HttpService httpservice;
290 private final HttpServerConnection conn;
291
292 public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) {
293 super();
294 this.httpservice = httpservice;
295 this.conn = conn;
296 }
297
298 public void run() {
299 System.out.println("New connection thread");
300 HttpContext context = new BasicHttpContext(null);
301 try {
302 while (!Thread.interrupted() && this.conn.isOpen()) {
303 this.httpservice.handleRequest(this.conn, context);
304 }
305 } catch (ConnectionClosedException ex) {
306 System.err.println("Client closed connection");
307 } catch (IOException ex) {
308 System.err.println("I/O error: " + ex.getMessage());
309 } catch (HttpException ex) {
310 System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
311 } finally {
312 try {
313 this.conn.shutdown();
314 } catch (IOException ignore) {
315 }
316 }
317 }
318
319 }
320
321}