Initial Commit
commit
f77d40cc0f
|
@ -0,0 +1,10 @@
|
|||
.AppleDouble
|
||||
.DS_Store
|
||||
|
||||
*/.AppleDouble
|
||||
*/.DS_Store
|
||||
*.pyc
|
||||
|
||||
**/*.pyc
|
||||
|
||||
tmp
|
|
@ -0,0 +1,9 @@
|
|||
from lnwsgi.exceptions import *
|
||||
|
||||
import lnwsgi.status
|
||||
import lnwsgi.walkable
|
||||
import lnwsgi.authentication
|
||||
|
||||
from lnwsgi.request import WSGIRequest
|
||||
from lnwsgi.application import WSGIApplication
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import sys
|
||||
import traceback
|
||||
import io
|
||||
|
||||
import lnwsgi
|
||||
from lnwsgi.exceptions import WebException
|
||||
|
||||
|
||||
class WSGIApplication:
|
||||
|
||||
def __init__(self, appName = "WSGIApplication"):
|
||||
self.__root = lnwsgi.walkable.Walkable()
|
||||
self.url = None
|
||||
self.appName = appName
|
||||
|
||||
def root(self):
|
||||
return self.__root
|
||||
|
||||
def setRoot(self, r):
|
||||
self.__root = r
|
||||
|
||||
def setApplicationURL(self, url):
|
||||
self.url = url
|
||||
|
||||
|
||||
def authenticate(self, request):
|
||||
return lnwsgi.authentication.Identity("", [])
|
||||
|
||||
|
||||
def handle(self, request):
|
||||
# try:
|
||||
path = list( filter( None, request.path.split("/") ) )
|
||||
|
||||
response = self.__root.walk( request, path )
|
||||
|
||||
if isinstance(response, str):
|
||||
response = ( response.encode(), )
|
||||
|
||||
return response
|
||||
# except:
|
||||
# ei = sys.exc_info()
|
||||
# request.setResponseHeader("content-type","text/html")
|
||||
# return ("<html><body>{0}: {1}</body></html>".format(ei[0].__name__,ei[1]).encode(),)
|
||||
|
||||
def handleException(self, request, exc_info = None):
|
||||
if exc_info is None:
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
m = io.StringIO()
|
||||
|
||||
m.write("Exception: {0}\n".format(exc_info[0].__name__))
|
||||
m.write("Message: {0}\n".format(exc_info[1]))
|
||||
m.write("Traceback: {0}\n".format(
|
||||
"\n ".join( traceback.extract_tb(exc_info[2]).format() )
|
||||
))
|
||||
|
||||
if isinstance(exc_info[1], WebException ):
|
||||
status = exc_info[1].status
|
||||
else:
|
||||
status = 500
|
||||
|
||||
if status == 401:
|
||||
request.setResponseHeader("WWW-Authenticate","Basic realm=\"{0}\", charset=\"UTF-8\"".format(self.appName))
|
||||
|
||||
request.setResponseHeader("content-type","text/plain")
|
||||
request.status(status)
|
||||
|
||||
return (m.getvalue().encode(),)
|
||||
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
request = lnwsgi.WSGIRequest( self, environ )
|
||||
print("R: {0:16} {1}".format( str(request.identity()), request ))
|
||||
|
||||
try:
|
||||
request.identity().authorize( request )
|
||||
response = self.handle( request )
|
||||
except:
|
||||
response = self.handleException( request, sys.exc_info() )
|
||||
|
||||
|
||||
start_response(
|
||||
lnwsgi.status.getStatusMessage(request.status()),
|
||||
request.listResponseHeaders()
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import itertools
|
||||
|
||||
from lnwsgi.exceptions import Unauthorized,Unauthenticated
|
||||
|
||||
class Identity:
|
||||
|
||||
def __init__(self, name, rights):
|
||||
self.__name = name
|
||||
self.__rights = dict( rights )
|
||||
|
||||
def name(self):
|
||||
return self.__name
|
||||
|
||||
def rights(self):
|
||||
return dict(self.__rights)
|
||||
|
||||
def authorizePath(self, request, rpath):
|
||||
|
||||
print("authorizePath(): {0} => {1}".format(rpath, request.path))
|
||||
|
||||
p = itertools.zip_longest(rpath.split("/"),request.path.split("/"))
|
||||
for pi in p:
|
||||
if pi[1] is None:
|
||||
return True
|
||||
if pi[0] == pi[1]:
|
||||
continue
|
||||
if pi[0] == "*":
|
||||
continue
|
||||
if pi[0] == "**":
|
||||
return True
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
def authorize(self, request):
|
||||
for r in self.__rights:
|
||||
if self.authorizePath( request, r ):
|
||||
return self.__rights[r]
|
||||
|
||||
if self.__name == "":
|
||||
raise Unauthenticated( request )
|
||||
raise Unauthorized( request )
|
||||
|
||||
def __str__(self):
|
||||
return self.__name
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
class WebException(Exception):
|
||||
|
||||
def __init__(self, message, status = 500):
|
||||
Exception.__init__( self, message )
|
||||
self.status = status
|
||||
|
||||
|
||||
class UnsupportedMediaType(WebException):
|
||||
pass
|
||||
|
||||
class ParameterException(WebException):
|
||||
|
||||
def __init__(self, name):
|
||||
WebException.__init__(self, "value of parameter [{0}] is unsupported".format(name))
|
||||
|
||||
class Unauthenticated(WebException):
|
||||
def __init__(self, request):
|
||||
WebException.__init__(self, "unauthorized to access {0}".format(request.path), status = 401)
|
||||
|
||||
class Unauthorized(WebException):
|
||||
|
||||
def __init__(self, request):
|
||||
WebException.__init__(self, "unauthorized to access {0}".format(request.path), status = 403)
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
import sys
|
||||
import io
|
||||
import json
|
||||
|
||||
from lnwsgi.reqvar import RequestParameters
|
||||
|
||||
class WSGIRequest:
|
||||
|
||||
def __init__(self,application,environ):
|
||||
self.__application = application
|
||||
self.__environ = environ
|
||||
self.__scheme = self.__environ[ "wsgi.url_scheme" ].lower()
|
||||
|
||||
self.method = self.__environ["REQUEST_METHOD"].upper()
|
||||
|
||||
if "HTTP_HOST" in self.__environ:
|
||||
self.__hostname = self.__environ[ "HTTP_HOST" ]
|
||||
else:
|
||||
self.__hostname = self.__environ[ "SERVER_NAME" ]
|
||||
|
||||
self.__port = int(self.__environ[ "SERVER_PORT" ])
|
||||
self.__script = self.__environ[ "SCRIPT_NAME" ]
|
||||
self.path = self.__environ[ "PATH_INFO" ]
|
||||
|
||||
if "QUERY_STRING" in self.__environ:
|
||||
self.__query = self.__environ[ "QUERY_STRING" ]
|
||||
else:
|
||||
self.__query = ""
|
||||
|
||||
if "CONTENT_TYPE" in self.__environ:
|
||||
self.content_type = self.__environ["CONTENT_TYPE"]
|
||||
|
||||
if "wsgi.input" in self.__environ:
|
||||
self.__content = self.__environ["wsgi.input"].read()
|
||||
else:
|
||||
self.__content = bytes()
|
||||
|
||||
self.parameters = RequestParameters( self )
|
||||
|
||||
self.__status = 200
|
||||
self.__response_headers = {}
|
||||
self.setResponseHeader("content-type","text/html")
|
||||
|
||||
self.__walked = []
|
||||
self.__interpretAccepts()
|
||||
|
||||
self.__identity = application.authenticate( self )
|
||||
|
||||
def identity(self):
|
||||
return self.__identity
|
||||
|
||||
def __interpretAccepts(self):
|
||||
self.__accepts = []
|
||||
|
||||
if "HTTP_ACCEPTS" in self.__environ:
|
||||
al = self.__environ["HTTP_ACCEPTS"].split(",")
|
||||
for a in al:
|
||||
t = a.split(";")
|
||||
mtype = t[0].strip()
|
||||
|
||||
self.__accepts.append( mtype.lower() )
|
||||
|
||||
def __contains__(self, name):
|
||||
return name in self.__environ
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.__environ[name]
|
||||
|
||||
def __iter__(self):
|
||||
return self.__environ.__iter__()
|
||||
|
||||
def getOrNone(self, name):
|
||||
if name in self:
|
||||
return self[name]
|
||||
return None
|
||||
|
||||
def accepts(self):
|
||||
return list( self.__accepts )
|
||||
|
||||
def status(self, sc = None):
|
||||
if not sc is None:
|
||||
self.__status = sc
|
||||
return self.__status
|
||||
|
||||
def content(self):
|
||||
return self.__content
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.__content.decode())
|
||||
|
||||
def query(self):
|
||||
return self.__query
|
||||
|
||||
def pushWalkedObject(self, wo):
|
||||
self.__walked.append( wo )
|
||||
|
||||
def walkedObjects(self):
|
||||
return list( self.__walked )
|
||||
|
||||
|
||||
def self(self, path = None):
|
||||
if self.__application.url is None:
|
||||
url = "{0}://{1}".format(self.__scheme,self.__hostname)
|
||||
else:
|
||||
url = self.__application.url
|
||||
# if not ((self.__scheme == "http" and self.__port == 80) or (self.__scheme == "https" and self.__port == 443)):
|
||||
# url = "{0}:{1}".format(url, self.__port)
|
||||
|
||||
if path is None:
|
||||
path = self.path
|
||||
|
||||
url = "{0}{1}{2}".format(url,self.__script,path)
|
||||
|
||||
return url;
|
||||
|
||||
def getResponseHeaders(self):
|
||||
return dict( self.__response_headers )
|
||||
|
||||
def listResponseHeaders(self):
|
||||
rh = []
|
||||
for n in self.__response_headers:
|
||||
rh.append( (n, self.__response_headers[n] ) )
|
||||
return rh
|
||||
|
||||
def setResponseHeader(self, name, value):
|
||||
if value is None:
|
||||
del self.__response_headers[ name.lower() ]
|
||||
else:
|
||||
self.__response_headers[ name.lower() ] = value
|
||||
|
||||
def __str__(self):
|
||||
s = io.StringIO()
|
||||
s.write("{method:8} {path}".format(**self.__dict__))
|
||||
return s.getvalue()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
from urllib.parse import unquote_plus
|
||||
|
||||
class RequestParameter:
|
||||
|
||||
def __init__(self,name,value = None,headers = {}):
|
||||
self.__name = name
|
||||
self.__value = value
|
||||
self.__headers = headers
|
||||
|
||||
def name(self):
|
||||
return self.__name
|
||||
def value(self):
|
||||
return self.__value
|
||||
def headers(self):
|
||||
return self.__headers
|
||||
|
||||
def filename(self):
|
||||
pp = self.__headers.parameters("CONTENT-DISPOSITION")
|
||||
if (not pp is None) and ("filename" in pp.keys()):
|
||||
return pp["filename"]
|
||||
return None
|
||||
|
||||
def save(self, filename):
|
||||
f = open( filename, "wb" )
|
||||
f.write( self.__value )
|
||||
f.close()
|
||||
|
||||
|
||||
class RequestParameters:
|
||||
|
||||
def __init__(self, request):
|
||||
self.__request = request
|
||||
self.__values = {}
|
||||
|
||||
self.__append_query()
|
||||
self.__append_mime()
|
||||
|
||||
def __getitem__(self,name):
|
||||
return self.__values[name][0]
|
||||
|
||||
def __contains__(self,name):
|
||||
return name in self.__values
|
||||
|
||||
def get(self,name):
|
||||
try:
|
||||
return self[name].value()
|
||||
except:
|
||||
return None
|
||||
|
||||
def getOrDefault(self, name, default = None):
|
||||
if name in self:
|
||||
return self[name].value()
|
||||
return default
|
||||
|
||||
|
||||
def keys(self):
|
||||
return self.__values.keys()
|
||||
|
||||
def list(self,name):
|
||||
if name in self.__values:
|
||||
return list(self.__values[name])
|
||||
return []
|
||||
|
||||
def __append_var(self,var):
|
||||
if not var.name() in self.__values.keys():
|
||||
self.__values[var.name()] = []
|
||||
self.__values[var.name()].append( var )
|
||||
|
||||
def __append_mime(self):
|
||||
pass
|
||||
# mime = self.__request.mime()
|
||||
#
|
||||
# if (mime.multipart()):
|
||||
# for p in mime.parts():
|
||||
# n = p.name()
|
||||
# if not n is None:
|
||||
# self.__append_var(FormValue(n,p.body(),p.headers()))
|
||||
|
||||
def __append_query(self):
|
||||
qs = self.__request.query()
|
||||
|
||||
pairs = qs.split("&")
|
||||
for pair in pairs:
|
||||
sp = pair.split("=")
|
||||
if (len(sp)>0):
|
||||
name = sp[0]
|
||||
if (len(sp)>1):
|
||||
value = sp[1]
|
||||
else:
|
||||
value= ""
|
||||
|
||||
self.__append_var( RequestParameter( unquote_plus(name), unquote_plus(value)) )
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
status_messages = {
|
||||
200: "OK",
|
||||
401: "Unauthenticated",
|
||||
403: "Unauthorized",
|
||||
404: "NotFound",
|
||||
500: "Exception was raised while handling your request"
|
||||
}
|
||||
|
||||
def getStatusMessage(statuscode):
|
||||
if statuscode in status_messages:
|
||||
return "{0} {1}".format(statuscode,status_messages[statuscode])
|
||||
return "{0} unknown status".format(statuscode)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
from lnwsgi.walkable.walkable import Walkable,CachedWalkable,Callable,TypeAware
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
import sys
|
||||
import time
|
||||
|
||||
from lnwsgi.exceptions import WebException
|
||||
|
||||
class TypeAware:
|
||||
|
||||
def media(self, mimeType, request):
|
||||
raise UnsupportedMediaType( mimeType )
|
||||
|
||||
def __call__(self, request):
|
||||
for accepted in request.accepts():
|
||||
try:
|
||||
return self.media( accepted )
|
||||
except UnsupportedMediaType:
|
||||
pass
|
||||
|
||||
raise UnsupportedMediaType(", ".join(request.accepts()))
|
||||
|
||||
class Walkable:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def walk(self, request, path = []):
|
||||
if len(path) == 0:
|
||||
return self( request )
|
||||
|
||||
np = path.pop(0)
|
||||
next = None
|
||||
|
||||
try:
|
||||
print("___ = {0} : {1}".format(str(self),np))
|
||||
next = getattr( self, np )
|
||||
except AttributeError:
|
||||
if len(path) == 0 and request.method == "PUT":
|
||||
return self.PUT( request, np )
|
||||
raise WebException("Not Found", status = 404)
|
||||
|
||||
if not isinstance( next, Walkable ):
|
||||
raise TypeError("Walkable needed")
|
||||
|
||||
return next.walk( request, path )
|
||||
|
||||
def GET(self, request):
|
||||
return ("No Implementation here.".encode(),)
|
||||
|
||||
def PUT(self, request, name):
|
||||
raise WebException("Object already exists!")
|
||||
|
||||
POST = GET
|
||||
PUT = GET
|
||||
HEAD = GET
|
||||
DELETE = None
|
||||
|
||||
def __call__(self, request):
|
||||
method = getattr(self, request.method)
|
||||
return method( request )
|
||||
|
||||
class CachedWalkable(Walkable):
|
||||
|
||||
def __init__(self):
|
||||
Walkable.__init__(self)
|
||||
|
||||
self.__cache_size = 16 # Maximum number of entries within cache
|
||||
self.__cache = {} # key -> value table
|
||||
self.__cache_names = [] # List of all cached names (youngest accessed #0)
|
||||
|
||||
def _touchCache(self, name):
|
||||
if name in self.__cache_names:
|
||||
self.__cache_names.remove( name )
|
||||
self.__cache_names.insert(0, name)
|
||||
if len(self.__cache_names) > self.__cache_size:
|
||||
self._rmCachedObject( self.__cache_names[-1] )
|
||||
|
||||
def _addCachedObject(self, name, value):
|
||||
self.__cache[ name ] = value
|
||||
self._touchCache( name )
|
||||
|
||||
def _rmCachedObject(self, name):
|
||||
del self.__cache[ name ]
|
||||
self.__cache_names.remove( name )
|
||||
|
||||
|
||||
|
||||
def lookup(self,name):
|
||||
raise AttributeError(name)
|
||||
|
||||
def __getattr__(self,name):
|
||||
if name in self.__cache_names:
|
||||
self._touchCache( name )
|
||||
return self.__cache[ name ]
|
||||
|
||||
o = self.lookup(name)
|
||||
self._addCachedObject( name, o )
|
||||
return o
|
||||
|
||||
|
||||
class Callable(Walkable):
|
||||
|
||||
def __init__(self, m):
|
||||
self.__method = m
|
||||
|
||||
def __call__(self, s, request):
|
||||
return self.__method( s, request )
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue