package http.server; import http.gen.HTTPReq; import http.gen.HTTPResp; import java.io.IOException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import edu.neu.ccs.demeterf.demfgen.lib.Map; import edu.neu.ccs.demeterf.demfgen.lib.List; /** Represents the Dispatch portion of an HTTP Server. Given a Map of Paths to * Methods, it is responsible for calling methods when given a specific request. */ public class ServerThread extends Thread { static void p(String s){ Factory.p(s); } /** Set to true in order to get Output for Server/Dispatch descisions. */ public static void setVerbose(boolean v){ Factory.setVerbose(v); } private ServerSocket socket; private Map dispatch; private Object target; private List servants = List.create(); private boolean single = false; private boolean done = false; /** Create a ServerThread... */ protected ServerThread(Server serv, boolean sing, Map disp, Object hand) throws IOException { socket = new ServerSocket(serv.value()); dispatch = disp; target = hand; single = sing; start(); } /** Run!! */ public void run(){ while (!done) { try { p("Waiting for connection..."); Socket req = socket.accept(); p("Got One from: " + req.getInetAddress() + ":" + req.getPort()); DispatchThread t = new DispatchThread(req, dispatch, target, this); addServant(t); t.start(); try{ if(single)t.join(); }catch(InterruptedException ie){ } } catch (IOException e) { if (!done) { System.err.println(" ServerThread Exception: " + e.getMessage()); done = true; } } } } /** Add a Servant Thread to the List */ public synchronized void addServant(Thread t){ servants = servants.push(t); } /** Remove a Servant Thread from the List */ public synchronized void removeServant(Thread t){ servants = servants.remove(t); this.notify(); } /** Await the completion of all Servant Threads */ public synchronized void waitServants(){ p("Waiting for Servants..."); while(!servants.isEmpty()){ try{ this.wait(1000); }catch(InterruptedException ie){} p("Still Waiting..."); } p("Done"); } /** Kill the Server listening thread, though workers will continue/complete */ public void shutdown() throws IOException{ done = true; socket.close(); waitServants(); } /** Handles the dispatch of a Request to a Server Method */ private static class DispatchThread extends Thread { Socket sock; Map dispatch; Object target; ServerThread parent; DispatchThread(Socket s, Map disp, Object targ, ServerThread p) { sock = s; dispatch = disp; target = targ; parent = p; } public void run(){ p("In Dispatch Thread..."); try { HTTPReq req = HTTPReq.fromSocket(sock); String path = req.trimmedUrl(); p("HTTP Parsed: " + path); HTTPResp res; if (!dispatch.containsKey(path)){ p("Unbound Path '"+path+"' Trying Default"); path = Path.EMPTY; } if (!dispatch.containsKey(path)){ p("No Default Path"); res = HTTPResp.error(); } else { Method m = dispatch.get(path); if (m.getParameterTypes().length == 0) res = (HTTPResp) m.invoke(target, new Object[] {}); else res = (HTTPResp) m.invoke(target, new Object[] { req }); } res.toSocket(sock); sock.close(); parent.removeServant(this); } catch (Exception e){ throw new RuntimeException(e); } } } }