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