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