namespace edu.neu.ccs.demeterf.http.server{ using System.Reflection; using edu.neu.ccs.demeterf.lib; using edu.neu.ccs.demeterf.util; using edu.neu.ccs.demeterf.http.classes; using System; using System.Net.Sockets; public class ServerDispatch{ public static void p(String s){ Factory.p(s); } private Map dispatch; private Object target; private ServerDispatch(Map dis, Object targ){ dispatch = dis; target = targ; } /** Create a dispatcher, selecting methods with the default argument types. */ public static ServerDispatch create(Object handler){ return create(handler,DefaultFormals); } /** Create a dispatcher, selecting methods with the given formal argument types. */ public static ServerDispatch create(Object handler, Type[] formals){ Type t = handler.GetType(); if (!t.IsDefined(typeof(Server),true)) { throw Factory.error("Cannot Create Server for unannotated class '" + t.Name + "'"); } return new ServerDispatch(List.create(t.GetMethods(Util.ReflectFlags)) .fold(new Folder(formals), Map.create()), handler); } public class Folder : List.Fold>{ public Folder(Type[] f){ formals = f; } Type[] formals; public override Map fold(MethodInfo m, Map res){ if(!m.IsDefined(typeof(Path),true)) return res; String path = check(m, formals); p("Mapping '" + path + "' to " + m.Name); return res.put(path, m); } } /** Handle a given HTTP request using this dispatcher (no socket) */ public HTTPResp handle(HTTPReq req){ return handle(req,null); } /** Handle a given HTTP request using this dispatcher, passing the * server's socket */ public HTTPResp handle(HTTPReq req, Socket sock){ try { String path = req.trimmedUrl(); p("HTTP Path: " + path); HTTPResp res; if (!dispatch.containsKey(path)){ p("Unbound Path '"+path+"' Trying Default"); path = Path.EMPTY; } Object[] args; if (!dispatch.containsKey(path)){ p("No Default Path"); res = HTTPResp.error(); } else { MethodInfo m = dispatch.get(path); switch(m.GetParameters().Length){ case 0:args = new Object[] {};break; case 1:args = new Object[] { req };break; default: if(sock == null) throw Factory.error("Missing Expected Socket Argument"); args = new Object[] { req, sock };break; } res = (HTTPResp) m.Invoke(target, args); } return res; }catch(MethodAccessException e){ return HTTPResp.error("Inaccessable Handler:"+e); }catch(Exception e){ return HTTPResp.error(""+e); } } /** Minimal Parameters... */ public static Type[] MinimalFormals = { typeof(HTTPReq) }; /** Allowed Parameters... */ public static Type[] DefaultFormals = { typeof(HTTPReq), typeof(Socket) }; static Type[] parameterTypes(MethodInfo m) { ParameterInfo[] ps = m.GetParameters(); Type[] ts = new Type[ps.Length]; for(int i = 0; i < ps.Length; i++) ts[i] = ps[i].ParameterType; return ts; } /** Do a sanity check of the types of an annotated method */ private static String check(MethodInfo m, Type[] formals){ Path p = (Path)m.GetCustomAttributes(typeof(Path),true)[0]; if (!typeof(HTTPResp).Equals(m.ReturnType)){ throw Factory.error("Return Type is incorrect: '" + m.ReturnType + "'"); } Type[] pars = parameterTypes(m); if (pars.Length > formals.Length) { throw Factory.error("Too many Parameters for Server Method '" + m.Name + "'"); } checkParams(m.Name, 0, pars, formals); return p.value; } /** Check that the parameters of the method are fine... */ private static void checkParams(String method, int i, Type[] ps, Type[] fmls){ if (i >= ps.Length || i >= fmls.Length)return; if (!fmls[i].IsAssignableFrom(ps[i])) { throw Factory.error("Parameter #" + i + " of " + method + " is of an incorrect type. " + "Expecting '" + fmls[i].Name + "'"); } checkParams(method, i+1, ps, fmls); } } }