package edu.neu.ccs.demeterf.http.server; import edu.neu.ccs.demeterf.http.classes.HTTPReq; import edu.neu.ccs.demeterf.http.classes.HTTPResp; import edu.neu.ccs.demeterf.util.Util; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import edu.neu.ccs.demeterf.lib.List; import java.lang.reflect.Method; /** Represents the Listening/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); } static final int DEFAULT_BACKLOG = 20; private ServerSocket socket; private ServerDispatch dispatch; private List servants = List.create(); private boolean single; private long maxRequest; private int backlog; private boolean done = false; private Method exHandler; /** Create a ServerThread... */ protected ServerThread(int port, boolean sing, ServerDispatch disp, long max) throws IOException { this(port, sing, disp, max, null, DEFAULT_BACKLOG); } /** Create a ServerThread... */ protected ServerThread(int port, boolean sing, ServerDispatch disp, long max, Method eh, int back) throws IOException { socket = new ServerSocket(port,back); dispatch = disp; single = sing; maxRequest = max; backlog = back; exHandler = eh; start(); } /** Run!! */ public void run(){ while (!done) { try { p("Waiting for connection..."); Socket req = socket.accept(); p("Got One from: " + req.getInetAddress() + ":" + req.getPort()); if(numServants() < backlog){ DispatchThread t = new DispatchThread(req, dispatch, this); addServant(t); t.start(); try{ if(single)t.join(); }catch(InterruptedException ie){ } }else{ // I would give an error response, but that takes too long req.close(); } Thread.yield(); } 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(); } /** Remove a Servant Thread from the List */ public synchronized int numServants(){ return servants.length(); } /** 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{ shutdown(true); } /** Kill the Server listening thread, maybe wait for workers */ public void shutdown(boolean wait) throws IOException{ done = true; socket.close(); if(wait) waitServants(); } /** Handles the dispatch of a Request to a Server Method */ private static class DispatchThread extends Thread { Socket sock; ServerDispatch dispatch; ServerThread parent; DispatchThread(Socket s, ServerDispatch disp, ServerThread p) { sock = s; dispatch = disp; parent = p; } public void run(){ p("In Dispatch Thread..."); HTTPReq req = null; try { req = HTTPReq.fromSocket(sock,2000,parent.maxRequest); }catch(Exception e){ if(parent.exHandler != null){ try{ HTTPResp res = Util.applyMethod(parent.exHandler, dispatch.getTarget(), new Object[]{e,sock}); res.toSocket(sock); parent.removeServant(this); sock.close(); return; }catch (Exception ee){ e = ee; } return; } this.parent.removeServant(this); try{ sock.close(); }catch(Exception ee){} return; } try{ HTTPResp res = dispatch.handle(req, sock); res.toSocket(sock); sock.close(); }catch (Exception e){ this.parent.removeServant(this); throw new ResponseException(e); } parent.removeServant(this); } } }