patx/mrhttp-asgi
Add static_cached()
Commit e852af9 · Mark Reed · 2024-03-19T10:17:17-07:00
Comments
No comments yet.
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)