/// <summary> /// Read in what is basically a text file and return a ResponsePacket with the text UTF8 encoded. /// </summary> protected ResponsePacket FileLoader(Route routeHandler, Session session, Dictionary <string, object> kvParams, string fullPath, string ext, ExtensionInfo extInfo) { ResponsePacket ret; if (!File.Exists(fullPath)) { ret = new ResponsePacket() { Error = Server.ServerError.FileNotFound }; Console.WriteLine("!!! File not found: " + fullPath); } else { string text = File.ReadAllText(fullPath); ret = new ResponsePacket() { Data = Encoding.UTF8.GetBytes(text), ContentType = extInfo.ContentType, Encoding = Encoding.UTF8 }; } return(ret); }
/// <summary> /// Load an HTML file, taking into account missing extensions and a file-less IP/domain, which should default to index.html. /// </summary> protected ResponsePacket PageLoader(Route routeHandler, Session session, Dictionary<string, object> kvParams, string fullPath, string ext, ExtensionInfo extInfo) { ResponsePacket ret; if (fullPath == WebsitePath) // If nothing follows the domain name or IP, then default to loading index.html. { ret = Route(session, GET, "/index.html", null); } else { if (String.IsNullOrEmpty(ext)) { // No extension, so we make it ".html" fullPath = fullPath + ".html"; } // Inject the "Pages" folder into the path // fullPath = WebsitePath +"\\Pages" + fullPath.RightOf(WebsitePath); // Custom, for page not found error. if (!File.Exists(fullPath)) { ret = new ResponsePacket() { Error = Server.ServerError.PageNotFound }; Console.WriteLine("!!! File not found: " + fullPath); } else { string text = File.ReadAllText(fullPath); // TODO: We put the route custom post process last because of how content is merged in the application's process, // but this might cause problems if the route post processor adds something that the app's post processor needs to replace. // How do we handle this? A before/after process? CSRF tokens are a great example! // Do the application global post process replacement. text = server.PostProcess(session, fullPath, text); // If a custom post process callback exists, call it. routeHandler.IfNotNull((r) => r.PostProcess.IfNotNull((p) => text = p(session, kvParams, text))); // Do our default post process to catch any final CSRF stuff in the fully merged document. text = server.DefaultPostProcess(session, fullPath, text); ret = new ResponsePacket() { Data = Encoding.UTF8.GetBytes(text), ContentType = extInfo.ContentType, Encoding = Encoding.UTF8 }; } } return ret; }
/// <summary> /// Read in an image file and returns a ResponsePacket with the raw data. /// </summary> protected ResponsePacket ImageLoader(Route routeHandler, Session session, Dictionary<string, object> kvParams, string fullPath, string ext, ExtensionInfo extInfo) { ResponsePacket ret; if (!File.Exists(fullPath)) { ret = new ResponsePacket() { Error = Server.ServerError.FileNotFound }; Console.WriteLine("!!! File not found: " + fullPath); } else { FileStream fStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fStream); ret = new ResponsePacket() { Data = br.ReadBytes((int)fStream.Length), ContentType = extInfo.ContentType }; br.Close(); fStream.Close(); } return ret; }
/// <summary> /// Read in what is basically a text file and return a ResponsePacket with the text UTF8 encoded. /// </summary> protected ResponsePacket FileLoader(Route routeHandler, Session session, Dictionary<string, object> kvParams, string fullPath, string ext, ExtensionInfo extInfo) { ResponsePacket ret; if (!File.Exists(fullPath)) { ret = new ResponsePacket() { Error = Server.ServerError.FileNotFound }; Console.WriteLine("!!! File not found: " + fullPath); } else { string text = File.ReadAllText(fullPath); ret = new ResponsePacket() { Data = Encoding.UTF8.GetBytes(text), ContentType = extInfo.ContentType, Encoding = Encoding.UTF8 }; } return ret; }
public ResponsePacket Route(Session session, string verb, string path, Dictionary<string, object> kvParams) { string ext = path.RightOfRightmostOf('.'); ExtensionInfo extInfo; ResponsePacket ret = null; verb = verb.ToLower(); path = path.ToLower(); if (extFolderMap.TryGetValue(ext, out extInfo)) { string wpath = path.Substring(1).Replace('/', '\\'); // Strip off leading '/' and reformat as with windows path separator. string fullPath = Path.Combine(WebsitePath, wpath); Route routeHandler = routes.SingleOrDefault(r => verb == r.Verb.ToLower() && path == r.Path.ToLower()); if (routeHandler != null) { // Application has a handler for this route. ResponsePacket handlerResponse = null; // If a handler exists: routeHandler.Handler.IfNotNull((h) => handlerResponse = h.Handle(session, kvParams)); // If multiple handlers exist, see which one, if any, is willing to handle the request. // We stop after the first handler. // This behavior is useful for web services or other types of routes where the data determines the route, not the URL. if (routeHandler.Handlers.Count > 0) { foreach (RouteHandler h in routeHandler.Handlers) { if (h.CanHandle(session, kvParams)) { handlerResponse = h.Handle(session, kvParams); break; } } } if (handlerResponse == null) { // Respond with default content loader. ret = extInfo.Loader(routeHandler, session, kvParams, fullPath, ext, extInfo); } else { // Respond with redirect. ret = handlerResponse; } } else { // Attempt default behavior ret = extInfo.Loader(null, session, kvParams, fullPath, ext, extInfo); } } else { ret = new ResponsePacket() { Error = Server.ServerError.UnknownType }; } return ret; }
private void Respond(HttpListenerRequest request, HttpListenerResponse response, ResponsePacket resp) { // Are we redirecting? if (String.IsNullOrEmpty(resp.Redirect)) { // No redirect. // Do we have a response? if (resp.Data != null) { // Yes we do. response.ContentType = resp.ContentType; response.ContentLength64 = resp.Data.Length; response.OutputStream.Write(resp.Data, 0, resp.Data.Length); response.ContentEncoding = resp.Encoding; } // Whether we do or not, no error occurred, so the response code is OK. // For example, we may have just processed an AJAX callback that does not have a data response. // Use the status code in the response packet, so the controller has an opportunity to set the response. response.StatusCode = (int)resp.StatusCode; } else { response.StatusCode = (int)HttpStatusCode.Redirect; if (String.IsNullOrEmpty(publicIP)) { string redirectUrl = request.Url.Scheme + "://" + request.Url.Host + resp.Redirect; response.Redirect(redirectUrl); } else { // response.Redirect("http://" + publicIP + resp.Redirect); string redirectUrl = request.Url.Scheme + "://" + request.Url.Host + resp.Redirect; response.Redirect(redirectUrl); } } response.OutputStream.Close(); }
/// <summary> /// Await connections. /// </summary> private async void StartConnectionListener(HttpListener listener) { ResponsePacket resp = null; // Wait for a connection. Return to caller while we wait. HttpListenerContext context = await listener.GetContextAsync(); Session session = sessionManager.GetSession(context.Request.RemoteEndPoint); OnRequest.IfNotNull(r => r(session, context)); // Release the semaphore so that another listener can be immediately started up. sem.Release(); Log(context.Request); HttpListenerRequest request = context.Request; try { string path = request.RawUrl.LeftOf("?"); // Only the path, not any of the parameters string verb = request.HttpMethod; // get, post, delete, etc. string parms = request.RawUrl.RightOf("?"); // Params on the URL itself follow the URL and are separated by a ? Dictionary <string, object> kvParams = GetKeyValues(parms); // Extract into key-value entries. string data = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding).ReadToEnd(); // GetKeyValues(data, kvParams); kvParams["Data"] = data; Log(kvParams); if (!VerifyCsrf(session, verb, kvParams)) { Console.WriteLine("CSRF did not match. Terminating connection."); context.Response.OutputStream.Close(); } else { resp = router.Route(session, verb, path, kvParams); // Update session last connection after getting the response, as the router itself validates session expiration only on pages requiring authentication. session.UpdateLastConnectionTime(); if (resp.Error != ServerError.OK) { resp.Redirect = OnError(resp.Error); } // TODO: Nested exception: is this best? try { Respond(request, context.Response, resp); } catch (Exception reallyBadException) { // The response failed! // TODO: We need to put in some decent logging! Console.WriteLine(reallyBadException.Message); } } } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); resp = new ResponsePacket() { Redirect = OnError(ServerError.ServerError) }; } }
/// <summary> /// Await connections. /// </summary> private async void StartConnectionListener(HttpListener listener) { ResponsePacket resp = null; // Wait for a connection. Return to caller while we wait. HttpListenerContext context = await listener.GetContextAsync(); Session session = sessionManager.GetSession(context.Request.RemoteEndPoint); OnRequest.IfNotNull(r => r(session, context)); // Release the semaphore so that another listener can be immediately started up. sem.Release(); Log(context.Request); HttpListenerRequest request = context.Request; try { string path = request.RawUrl.LeftOf("?"); // Only the path, not any of the parameters string verb = request.HttpMethod; // get, post, delete, etc. string parms = request.RawUrl.RightOf("?"); // Params on the URL itself follow the URL and are separated by a ? Dictionary<string, object> kvParams = GetKeyValues(parms); // Extract into key-value entries. string data = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding).ReadToEnd(); // GetKeyValues(data, kvParams); kvParams["Data"] = data; Log(kvParams); if (!VerifyCsrf(session, verb, kvParams)) { Console.WriteLine("CSRF did not match. Terminating connection."); context.Response.OutputStream.Close(); } else { resp = router.Route(session, verb, path, kvParams); // Update session last connection after getting the response, as the router itself validates session expiration only on pages requiring authentication. session.UpdateLastConnectionTime(); if (resp.Error != ServerError.OK) { resp.Redirect = OnError(resp.Error); } // TODO: Nested exception: is this best? try { Respond(request, context.Response, resp); } catch (Exception reallyBadException) { // The response failed! // TODO: We need to put in some decent logging! Console.WriteLine(reallyBadException.Message); } } } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); resp = new ResponsePacket() { Redirect = OnError(ServerError.ServerError) }; } }
/// <summary> /// Return a ResponsePacket with the specified URL and an optional (singular) parameter. /// </summary> public ResponsePacket Redirect(string url, string parm = null) { ResponsePacket ret = new ResponsePacket() { Redirect = url }; parm.IfNotNull((p) => ret.Redirect += "?" + p); return ret; }
public ResponsePacket Route(Session session, string verb, string path, Dictionary <string, object> kvParams) { string ext = path.RightOfRightmostOf('.'); ExtensionInfo extInfo; ResponsePacket ret = null; verb = verb.ToLower(); path = path.ToLower(); if (extFolderMap.TryGetValue(ext, out extInfo)) { string wpath = path.Substring(1).Replace('/', '\\'); // Strip off leading '/' and reformat as with windows path separator. string fullPath = Path.Combine(WebsitePath, wpath); Route routeHandler = routes.SingleOrDefault(r => verb == r.Verb.ToLower() && path == r.Path.ToLower()); if (routeHandler != null) { // Application has a handler for this route. ResponsePacket handlerResponse = null; // If a handler exists: routeHandler.Handler.IfNotNull((h) => handlerResponse = h.Handle(session, kvParams)); // If multiple handlers exist, see which one, if any, is willing to handle the request. // We stop after the first handler. // This behavior is useful for web services or other types of routes where the data determines the route, not the URL. if (routeHandler.Handlers.Count > 0) { foreach (RouteHandler h in routeHandler.Handlers) { if (h.CanHandle(session, kvParams)) { handlerResponse = h.Handle(session, kvParams); break; } } } if (handlerResponse == null) { // Respond with default content loader. ret = extInfo.Loader(routeHandler, session, kvParams, fullPath, ext, extInfo); } else { // Respond with redirect. ret = handlerResponse; } } else { // Attempt default behavior ret = extInfo.Loader(null, session, kvParams, fullPath, ext, extInfo); } } else { ret = new ResponsePacket() { Error = Server.ServerError.UnknownType }; } return(ret); }