patx/mrhttp-asgi

Add static_cached_timer to update changed files

Commit 9e4ca93 · Mark · 2024-03-19T15:43:16-07:00

Changeset
9e4ca9354b06f46bcb84ff78eff5853b9079876b
Parents
25679325bec991081acd219ad15c2e027aa416fd

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/src/mrhttp/app.py b/src/mrhttp/app.py
index 64f7ca7..ea45879 100644
--- a/src/mrhttp/app.py
+++ b/src/mrhttp/app.py
@@ -8,7 +8,7 @@ import signal
 import asyncio
 import traceback
 import socket
-import os, sys, random, mrpacker
+import os, sys, random, mrpacker, time
 from glob import glob
 import multiprocessing
 import faulthandler
@@ -71,6 +71,7 @@ class Application(mrhttp.CApp):
     self._mrq = None
     self._mrq2 = None
     self._mrc = None
+    self.static_cached_files = []
     self.session_backend = "memcached"
     self.uses_session = False
     self.uses_mrq = False
@@ -137,19 +138,39 @@ class Application(mrhttp.CApp):
       params["type"]    = r[4]
       self.router.add_route( r[0], r[1], params )
 
+  #TODO Size limit on files?
+  def static_cached_timer(self):
+    ts = time.time() # Avoid race 
+    for item in self.static_cached_files:
+      fn = item[1]
+      if os.path.getmtime(fn) > self.static_cached_timestamp:
+        with open(fn, 'rb') as f:
+          b = f.read()
+          self.router.update_cached_route( [item[0], b] )
+    self.static_cached_timestamp = ts
+    self.loop.call_later(10, self.static_cached_timer) 
+        
+       
+  #TODO Use brotli'd files - if [path].br exists use that instead
   def static_cached(self, root, directory):
     def removeprefix( prefix, text ):
       if text.startswith(prefix):
           return text[len(prefix):]
 
+    if not os.path.isdir(directory):
+      print("WARNING: app.static_cached root dir does not exist")
+      return
     files = glob(os.path.join(directory, '**', '*'), recursive=True)
+    self.static_cached_timestamp = time.time()
     for fn in files:
       if os.path.isdir(fn): continue
       with open(fn, 'rb') as f:
         b = f.read()
+      
 
       if not root.startswith('/'): root = '/'+root
       uri = root+removeprefix(directory, fn)
+      self.static_cached_files.append( [uri,fn] )
       self.router.add_cached_route( uri, b )
 
   def _get_idle_and_busy_connections(self):
@@ -292,6 +313,7 @@ class Application(mrhttp.CApp):
 
   def _appStart(self):
     self.loop.call_soon(self.updateDateString)
+    self.loop.call_soon(self.static_cached_timer)
 
 
   def _run(self, *, host, port, num_workers=None, debug=None):
diff --git a/src/mrhttp/internals/module.h b/src/mrhttp/internals/module.h
index ff88879..b4d87ed 100644
--- a/src/mrhttp/internals/module.h
+++ b/src/mrhttp/internals/module.h
@@ -74,6 +74,7 @@ static PyGetSetDef Protocol_getset[] = {
 
 static PyMethodDef Router_methods[] = {
   {"setupRoutes", (PyCFunction)Router_setupRoutes, METH_NOARGS,   ""},
+  {"update_cached_route", (PyCFunction)Router_update_cached_route, METH_O,   ""},
   {NULL}
 };
 static PyMethodDef MrhttpApp_methods[] = {
diff --git a/src/mrhttp/internals/protocol.c b/src/mrhttp/internals/protocol.c
index e8f30f6..3feee68 100644
--- a/src/mrhttp/internals/protocol.c
+++ b/src/mrhttp/internals/protocol.c
@@ -896,6 +896,7 @@ PyObject* protocol_task_done(Protocol* self, PyObject* task)
   PyObject* result = Py_True;
   PipelineRequest *r;
 
+  // TODO?
   // task is the task that just finished.  
   //   If task is at the head of the Q then return the response.
   //     then continue looping through the Q and return a response for anything that is already done
diff --git a/src/mrhttp/internals/router.c b/src/mrhttp/internals/router.c
index 0eead7d..0c8d487 100644
--- a/src/mrhttp/internals/router.c
+++ b/src/mrhttp/internals/router.c
@@ -51,6 +51,25 @@ static int numInString( char c, char* s, int len ) {
   return ret;
 }
 
+PyObject* Router_update_cached_route(Router* self, PyObject* item) {
+
+  PyObject *path = PyList_GET_ITEM( item, 0 );
+  PyObject *b    = PyList_GET_ITEM( item, 1 );
+
+  Py_ssize_t plen;
+  char *p = PyUnicode_AsUTF8AndSize( path, &plen );
+
+  Route *r = self->staticRoutes;
+  for (int i = 0; i<self->numStaticRoutes; i++,r++ ) {
+    //DBG printf("request path len %d - %.*s\n", (int)request->path_len, (int)request->path_len, request->path);
+    //DBG printf("route path %.*s \n", (int)r->len, r->path);
+    if ( plen == r->len && !memcmp(r->path, p, plen) ) {
+      r->cached = b;
+      Py_RETURN_NONE; 
+    }
+  }
+  Py_RETURN_NONE; 
+}
 
 PyObject *Router_setupRoutes (Router* self) {
   //PyObject *sroutes = self->pyStaticRoutes; //PyObject_GetAttrString((PyObject*)self, "static_routes");    
diff --git a/src/mrhttp/internals/router.h b/src/mrhttp/internals/router.h
index ec10769..25887f2 100644
--- a/src/mrhttp/internals/router.h
+++ b/src/mrhttp/internals/router.h
@@ -40,5 +40,6 @@ PyObject *Router_new    (PyTypeObject* self, PyObject *args, PyObject *kwargs);
 int       Router_init   (Router* self, PyObject *args, PyObject *kwargs);
 void      Router_dealloc(Router* self);
 PyObject *Router_setupRoutes(Router* self);
+PyObject* Router_update_cached_route(Router* self, PyObject* item);
 
 Route* router_getRoute(Router* self, Request* request);
diff --git a/tst.py b/tst.py
index 25e40fb..f808765 100755
--- a/tst.py
+++ b/tst.py
@@ -4,7 +4,7 @@ from mrhttp import app
 #import asyncmrq, mrpacker
 
 app.config["memcache"] = [ ("127.0.0.1", 11211) ]
-app.static_cached("www","/home/ch/code/web/chatter/www")
+#app.static_cached("www","/path/to/www")
 
 #@app.on('at_start')
 #async def setup():