patx/mrhttp-asgi

Add static_cached()

Commit e852af9 · Mark Reed · 2024-03-19T10:17:17-07:00

Changeset
e852af9d299471154f229f3317bf2113a2c6e728
Parents
fb6ce04d1a3d27c425a0758e5844b15bf3bfb5f8

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 0a11ce9..64f7ca7 100644
--- a/src/mrhttp/app.py
+++ b/src/mrhttp/app.py
@@ -9,6 +9,7 @@ import asyncio
 import traceback
 import socket
 import os, sys, random, mrpacker
+from glob import glob
 import multiprocessing
 import faulthandler
 import functools
@@ -16,7 +17,7 @@ from wsgiref.handlers import format_date_time
 import inspect, copy
 #from inspect import signature #getmodulename, isawaitable, signature, stack
 #from prof import profiler_start,profiler_stop
-import uuid, http.cookies
+import http.cookies
 
 import mrhttp
 from mrhttp import Protocol
@@ -136,6 +137,20 @@ class Application(mrhttp.CApp):
       params["type"]    = r[4]
       self.router.add_route( r[0], r[1], params )
 
+  def static_cached(self, root, directory):
+    def removeprefix( prefix, text ):
+      if text.startswith(prefix):
+          return text[len(prefix):]
+
+    files = glob(os.path.join(directory, '**', '*'), recursive=True)
+    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.router.add_cached_route( uri, b )
 
   def _get_idle_and_busy_connections(self):
     return \
@@ -393,9 +408,6 @@ class Application(mrhttp.CApp):
 
     skey = userk + k[len(userk):]
 
-    # TODO We could have user id be optional and do this if not given
-    #skey = uuid.uuid4().hex
-
     # Send the session cookie back to the user  
     c = cookies
     c['mrsession'] = skey
diff --git a/src/mrhttp/internals/protocol.c b/src/mrhttp/internals/protocol.c
index d38862c..e8f30f6 100644
--- a/src/mrhttp/internals/protocol.c
+++ b/src/mrhttp/internals/protocol.c
@@ -562,6 +562,10 @@ Protocol* Protocol_handle_request(Protocol* self, Request* request, Route* r) {
     if ( self->request == request ) self->request = (Request*)MrhttpApp_get_request( self->app );
   }
 
+  // If we have cached bytes 
+  if ( r->cached ) {
+    if(!protocol_write_response(self, request, r->cached)) goto error;
+  }
   
   if(!(result = protocol_callPageHandler(self, r->func, request)) ) {
 
@@ -661,7 +665,7 @@ Protocol* Protocol_handle_request(Protocol* self, Request* request, Route* r) {
 
   if(!protocol_write_response(self, request, result)) goto error;
 
-  // TODO We aren't closing the connection if issued a connection: close header.  This is okay behind nginx
+  // TODO We aren't closing the connection if issued a connection: close header.  
   //if ( !request->keep_alive ) Protocol_close(self);
 
   Py_DECREF(result);
diff --git a/src/mrhttp/internals/router.c b/src/mrhttp/internals/router.c
index ae9f9cc..0eead7d 100644
--- a/src/mrhttp/internals/router.c
+++ b/src/mrhttp/internals/router.c
@@ -84,6 +84,10 @@ PyObject *Router_setupRoutes (Router* self) {
     o = PyDict_GetItemString( r, "type"  );
     if (o) rte->mtype = PyLong_AsLong(o);
     rte->user_key = PyDict_GetItemString( r, "user_key" );
+    rte->cached   = PyDict_GetItemString( r, "cached" );
+    if ( Py_True == PyDict_GetItemString( r, "cache" ) ) {
+      rte->cached = PyObject_CallFunctionObjArgs(handler, r, NULL);
+    }
 
     DBG printf(" path %.*s func ptr %p\n", (int)rte->len, rte->path, rte->func);
   }
diff --git a/src/mrhttp/internals/router.h b/src/mrhttp/internals/router.h
index f7bbd2f..ec10769 100644
--- a/src/mrhttp/internals/router.h
+++ b/src/mrhttp/internals/router.h
@@ -20,6 +20,7 @@ typedef struct {
   char mtype;
   int max_byte_size;
 
+  PyObject *cached;
   PyObject *user_key;
   //char *user_key;
   //long user_key_len;
diff --git a/src/mrhttp/router.py b/src/mrhttp/router.py
index 9365276..e1b9743 100644
--- a/src/mrhttp/router.py
+++ b/src/mrhttp/router.py
@@ -16,6 +16,7 @@ class Router(mrhttp.CRouter):
   def finalize_routes(self):
     self.routes.sort(key=lambda x: x["sortlen"],reverse=True)
 
+
   def add_route(self, handler, uri, methods=['GET'], options=[],_type="html"):
 
     if handler.__name__ in self.func_namemap:
@@ -48,6 +49,7 @@ class Router(mrhttp.CRouter):
     if "session" in options: r["session"] = True
     if "mrq" in options: r["mrq"] = True
     if "mrq2" in options: r["mrq2"] = True
+    if "cache" in options: r["cache"] = True
     if "append_user" in options: r["append_user"] = True
     # Static routes
     if not "{" in uri:
@@ -65,4 +67,14 @@ class Router(mrhttp.CRouter):
       r["num_segs"] = len(segs)
       self.routes.append( r )
 
+  def add_cached_route(self, uri, byts, _type="html"):
+    def fake_handler():
+      print("ERROR fake_handler called")
+    r = {}
+    r["path"]    = uri
+    r["methods"] = ["GET"]
+    r["handler"] = id(fake_handler)
+    r["cached"] = byts
+    r["type"] = 0
+    self.static_routes.append( r )
 
diff --git a/tests/lua/q-json.lua b/tests/lua/q-json.lua
index 9e35e33..ee93362 100755
--- a/tests/lua/q-json.lua
+++ b/tests/lua/q-json.lua
@@ -3,21 +3,27 @@
 init = function(args)
    local r = {}
    wrk.headers["Content-Type"] = "application/json; charset=utf-8"
-   r[1]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[2]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[3]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[4]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[5]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[6]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[7]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[8]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[9]  = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[10] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[11] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[12] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[13] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[14] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
-   r[15] = wrk.format('POST','/q/1/2/', {"Content-Type", "application/json"}, '{"my":"json"}')
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
+   table.insert(r, wrk.format('POST','/json', {"Content-Type", "application/json"}, '{"name":"json"}'))
    req = table.concat(r)
 end
 
diff --git a/todo b/todo
index f4bc67b..c9f6ef5 100644
--- a/todo
+++ b/todo
@@ -1,4 +1,7 @@
 
+Handle static files - static root route and path. 
+Connection buffer size - after a file upload shrink the buffer back down.  Behind cloudflare connections will remain open forever. Behind nginx its just one connection so who cares.  Out front client connections will close so it doesn't matter?
+
 
 support HEAD 
 
diff --git a/tst.py b/tst.py
index 7c36784..25e40fb 100755
--- a/tst.py
+++ b/tst.py
@@ -1,25 +1,20 @@
 
-import traceback
+import traceback, mrjson
 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.on('at_start')
 #async def setup():
   #app.c = asyncmrq.Client()
   #await app.c.connect(servers=[("127.0.0.1",7100)])
 
-#@app.route('/',options=['session'])
[email protected]('/')
-async def index(r):
-  print( r.headers )
-  #print( r.ip )
-  return "yay"  
-  #d = r.mrpack
-  #return d["name"]
-  #x = r.form
-  #return x["param2"]
+#@app.route('/')
[email protected]('/',options=['cache'])
+def index(r):
+  return "hello world"  
 
 @app.route('/123456789123456789')
 async def long(r):
@@ -27,12 +22,16 @@ async def long(r):
 
 @app.route('/json')
 def json(r):
-  return r.json["name"]
+  return mrjson.dumps({'message': 'Hello, world!'})
 
 @app.route('/mrpacker')
 def mrpacker(r):
   return r.mrpack["name"]
 
[email protected]('/{}/tst')
+def firstarg(r,a):
+  return a
+
 
 try:
   app.run(cores=4)