模块化http服务器:身份验证、缓存、代理等
mixnmatchttp的Python项目详细描述
概述
mixmatchttp
是一个基于python的http的模块化http/s服务器
服务器允许您"混合
"n"匹配"各种功能。它定义了几个请求处理程序
是http.server.simplehttpprequesthandler的包装器
一个threadinghttpserver
可以代替python的
http.server.httpserver
支持多线程。
快速启动
请求处理程序将特殊端点和/或模板定义为类属性。
端点是服务器的restful api。不映射到
端点被视为对文件或目录(python的http服务器)的请求
处理它,除非类重写do(get post…)
方法。
映射到终结点的请求将调用终结点处理程序:方法
你的类名为"path"的do{underscope separated path}
为其定义方法的最特定(最长)路径。例如。
/foo/bar/baz
将尝试调用do-foo-bar-baz
,然后调用do-foo-bar
,然后
dou foo
,最后是dou default
。do_default
在
basehttprequesthandler
但您的类可能要重写它。
模板页是参数化的响应体。模板指定模板 要与一起使用的页,并提供要与一起使用的参数和值的字典 模板。每个参数值还可以包含动态参数 给了来自模板的basehttprequesthandler.page方法,该方法 构成最后一页。
您可以从一个或多个*httprequesthandlers
继承。每个家长的
端点/模板将被复制到您的子类,而不会被覆盖'
端点/模板。
重要提示:
- 如果需要重写任何http方法处理程序(例如
do-get
), 必须用mixmatchttp.handlers.base.methodhandler来修饰它们,如下所示 显示在下面的演示中。如果你需要调用任何父的http 方法处理程序必须使用
包装属性,如演示所示。
定义端点
终结点、模板和模板页构造函数具有与
为了一本字典。终结点的类型为mixmatchttp.endpoints.endpoint
,
而模板和模板页是
mixmatchttp.common.dictnoclobber
。但是,您可以将它们定义为
字典,或任何具有类似字典的接口的类型,以及
basehttprequesthandler的元类将它们转换为适当的
类,
例如,您可以定义这样的端点:
classMyHandler(BaseHTTPRequestHandler):_endpoints=mixnmatchttp.endpoints.Endpoint(some_sub={'$allowed_methods':{'GET','POST'},'$nargs':1,'some_sub_sub':{'$nargs':endpoints.ARGS_ANY,'$raw_args':True,# don't canonicalize rest of path}},some_other_sub={})
或类似:
classMyHandler(BaseHTTPRequestHandler):_endpoints=mixnmatchttp.endpoints.Endpoint({'some_sub':{'$allowed_methods':{'GET','POST'},'$nargs':1,'some_sub_sub':{'$nargs':endpoints.ARGS_ANY,'$raw_args':True,# don't canonicalize rest of path}},},some_other_sub={})
或类似:
classMyHandler(BaseHTTPRequestHandler):_endpoints={'some_sub':{'$allowed_methods':{'GET','POST'},'$nargs':1,'some_sub_sub':{'$nargs':endpoints.ARGS_ANY,'$raw_args':True,# don't canonicalize rest of path}},'some_other_sub':{}}
以$
开头的任何关键字参数或字典键都对应于
属性(不带$
),该属性指定如何调用端点。
所有其他关键字参数/键都成为父项的子终结点;它们
值应该是另一个端点
(或任何类似字典的对象)。
默认端点属性为:
disabled=False|True# specifies if the enpoint cannot be called directly;# False for child endpoints but True for root endpointallowed_methods={'GET','HEAD'}# a set of allowed HTTP methods; HEAD is added to the# set if 'GET' is presentnargs=0# how many slash-separated arguments the endpoint can take;# can be a number of any of:# mixnmatchttp.endpoints.ARGS_OPTIONAL for 0 or 1# mixnmatchttp.endpoints.ARGS_ANY for any number# mixnmatchttp.endpoints.ARGS_REQUIRED for 1 or more# !!only reliable if raw_args is False!!raw_args=False# whether arguments should not be canonicalized,# e.g. /foo/..//bar/./baz will not be turned to /bar/baz
默认情况下启用子终结点,根终结点由禁用
默认值;如果要启用它,请手动更改已禁用
属性,或按如下方式构造:
classMyHandler(BaseHTTPRequestHandler):_endpoints={'some_sub':{...},'$disabled':False,# a request for / will now call do_ or do_default# instead of do_(GET|POST|...)}
处理分析的终结点
当路径解析为端点ep
时,对应的端点处理程序
(<代码> doy????)将传递一个参数:
amixmatchttp.endpoints.parsedendpoint
(继承自
mixmatchttp.endpoints.endpoint
)使用
以下附加属性:
httpreq
:此请求的basehttprequesthandler
实例handler
:选择了作为处理程序的httpreq
方法的一部分,其中 第一个参数是parsedendpoint
本身root
:端点的最长路径(带有前导/
)对应于 一个定义的处理程序,即如果路径是/foo/bar
和do-foo
,则 选中时,根
将是/foo
;如果使用do_default
,根
为空('
)。sub
:端点路径的其余部分,不带前导符/
,例如bar
或foo/bar
args
:所有遵循端点路径的内容都没有前导符/
argslen
:用它调用的参数数(数组长度从/
分隔args
)
示例:实现服务器
可能要重写的某些方法,以及实现自定义
端点和模板如下所示,用于myhandler
:
# from __future__ import unicode_literalsfrom__future__importprint_functionfrom__future__importdivisionfrom__future__importabsolute_importfrombuiltinsimport*fromfutureimportstandard_librarystandard_library.install_aliases()importreimportsslfromhttp.serverimportHTTPServerfrommixnmatchttp.serversimportThreadingHTTPServerfrommixnmatchttpimportendpointsfrommixnmatchttp.handlersimportBaseHTTPRequestHandler,methodhandlerfrommixnmatchttp.commonimportDictNoClobberclassMyHandler(BaseHTTPRequestHandler):_endpoints=endpoints.Endpoint(foobar={},# will use do_default handlerrefreshme={'$nargs':endpoints.ARGS_OPTIONAL,},debug={# these are for when /debug is called'$allowed_methods':{'GET','POST'},'$nargs':1,'sub':{# will use do_debug handler# these are for when /debug/sub is called'$nargs':endpoints.ARGS_ANY,'$raw_args':True,# don't canonicalize rest of path}},)_template_pages=DictNoClobber(simpletxt={'data':'$CONTENT','type':'text/html'},simplehtml={'data':''' <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> $HEAD <title>$TITLE</title> </head> <body> $BODY </body> </html> ''','type':'text/html'},)_templates=DictNoClobber(refresh={'fields':{'HEAD':'<meta http-equiv="refresh" content="${interval}">','TITLE':'Example','BODY':'<h1>Example page, will refresh every ${interval}s.</h1>',},'page':'simplehtml',},debug={'fields':{'CONTENT':'${info}You called endpoint $root ($sub) ($args)',},'page':'simpletxt',},)defdo_refreshme(self,ep):interval=ep.argsifnotinterval:interval='30''''Handler for the endpoint /refreshme'''page=self.page_from_template(self.templates['refresh'],{'interval':interval})self.render(page)defdo_debug(self,ep):'''Handler for the endpoint /debug'''# set a header just for this requestself.headers_to_send['X-Debug']='Foo'page=self.page_from_template(self.templates['debug'],{'root':ep.root,'sub':ep.sub,'args':ep.args})self.render(page)defdo_default(self,ep):'''Default endpoints handler'''page=self.page_from_template(self.templates['debug'],{'info':'This is do_default. ','root':ep.root,'sub':ep.sub,'args':ep.args})self.render(page)# Don't forget this decorator!@methodhandlerdefdo_GET(self):# Do something here, then call parent's undecorated methodsuper().do_GET.__wrapped__()defdenied(self):'''Deny access to /forbidden'''ifre.match('^/forbidden(/|$)',self.pathname):# return args are passed to BaseHTTPRequestHandler.send_error# in that order; both messages are optionalreturn(403,None,'Access denied')returnsuper().denied()defno_cache(self):'''Only allow caching of scripts'''return(notself.pathname.endswith('.js'))orsuper().no_cache()defsend_custom_headers(self):'''Send our custom headers'''self.send_header('X-Foo','Foobar')if__name__=="__main__":use_SSL=Falsekeyfile=''# path to PEM key, if use_SSL is Truecertfile=''# path to PEM certificate, if use_SSL is Truesrv_cls=HTTPServer# srv_cls = ThreadingHTTPServer # if using multi-threadingaddress='127.0.0.1'port=58080httpd=srv_cls((address,port),MyHandler)ifuse_SSL:httpd.socket=ssl.wrap_socket(httpd.socket,keyfile=keyfile,certfile=certfile,server_side=True)try:httpd.serve_forever()exceptKeyboardInterrupt:httpd.server_close()
- 请求
/debug/sub/../foo/../bar//baz
将调用self.do_debug
withep
,一份myhandler.\u endpoints['debug']['sub']
。ep.root
将为"debug",ep.sub
将为"sub",ep.args
将为"../foo//bar//baz"。self.command
将给出http方法。 - 请求
/debug/foo/./bar
将规范化/debug/bar
的路径(因为/debug
的raw_args
是false
)使用ep
调用dou debug
,这是myhandler的副本。
ep.root
将为"debug",ep.sub
将为"",ep.args
将为"bar"。 - 对
/debug/./bar
的请求将引发mixmatchttp.endpoints.notanendpointerror
,因为路径将被规范化(如上所述),并导致/bar
,并且bar
不是有效的端点。 - 请求
/foobar
将引发mixmatchttp.endpoints.methodnotallowederror
,因为/foobar
只允许httpget
- 请求
/debug/..foobar
将使用ep
调用父级的do-u default
,这是myhandler.\u endpoints['foobar']
的副本。ep.root
将是"",ep.sub
将是"foobar",ep.args
将是""。 - 请求
/debug/foo/bar
将引发mixmatchttp.endpoints.extraargserror
,因为/debug
只需要一个参数。 - 请求
/debug
将引发mixmatchttp.endpoints.missingargserror
,因为/debug
只需要一个参数。 - 请求
/refreshme
将呈现每30秒刷新一次的页面(默认)。 - 请求
/refreshme/5
将呈现每5秒刷新一次的页面。
处理程序
authhttprequesthandler
通过form或jsonpost
请求实现用户名:密码验证。具有需要身份验证的可配置文件路径/终结点。
如果未设置包含username:password的文件(作为\u userfile
类属性),则它将实现虚拟身份验证(所有登录都成功)。
get post/login
:如果用户名和密码有效,则发出一个会话
cookie- 支持的URL参数:
转到
:重定向到此URL
- 必需的body或url参数(除非未提供userfile,在这种情况下,它始终进行身份验证):
用户名
:用户名(duh)密码
:密码(duh)
- 响应代码:
200确定
:验证成功;会话
设置cookie401未经授权
:用户名或密码无效;302 found
:位置是通过goto
参数请求的
注:
- 重新启动时服务器端会忘记会话
- Cookie发出时带有
httponly
标志,如果通过SSL发出时也带有secure
标志
- 支持的URL参数:
获取/注销
:从浏览器和服务器中清除会话
cookie
- 支持的URL参数:
转到
:重定向到此URL
- 响应代码:
200正常
:空正文302 found
:位置是通过goto
参数请求的
get post/changepwd
:更改给定用户名的密码
- 支持的URL参数:
转到
:重定向到此URL
- 必需的正文或URL参数:
用户名
:用户名(duh)密码
:当前密码新密码
:新密码(duh)
- 响应代码:
200确定
:成功;密码已更改,当前会话
无效,并设置新的会话
cookie401未经授权
:用户名或密码无效302 found
:位置是通过goto
参数请求的
cachingtprequesthandler
允许将内容保存为命名页以供以后的请求使用,或将编码的(URL或Base64)请求内容显示为响应页。
post/echo
:呈现请求的内容- 支持的URL参数:
数据
:要呈现的页的编码内容(必需)键入
:呈现页面的内容类型(默认为文本/纯文本)
- 支持的格式:
应用程序/json
使用base64
编码数据应用程序/x-www-form-url encoded
(使用url编码的数据)
- 响应代码:
200好
:正文和内容类型都是按要求的
。400错误请求
:无法解码数据或查找数据参数
- 支持的URL参数:
post/cache/{name}
:临时保存请求的内容(仅在内存中)- 支持的URL参数和格式与
post/echo
- 响应代码:
204无内容
:页已缓存500服务器错误
:已达到最大缓存内存,或页面{name}已缓存
注:
- 保存后,即使页面已从内存中清除(请参见
/cache/clear
)
- 支持的URL参数和格式与
get/cache/{name}
:检索以前保存的页
- 响应代码:
200确定
:正文和内容类型
是在缓存期间请求的500服务器错误
:没有这样的缓存页或从内存中清除的页
获取/缓存/清除{name}
:清除以前保存的页以释放内存
- 响应代码:
204无内容
:页面已清除
获取/缓存/清除
:清除以前保存的所有页面以释放内存
- 响应代码:
204无内容
:所有页面均已清除
获取/缓存/新建
:获取随机uuid
- 响应代码:
200 ok
:主体包含随机生成的uuid;用于post/cache/{uuid}代码>
代理ttprequesthandler
重定向(使用307
)到URL中给定的任何地址。
get/goto/{address}
:重定向到此(uri解码)地址- 响应代码:
302 found
:location是在/goto/
之后的地址;如果没有给定域(即地址不是以架构://
或//
开头),则从referer
,origin
,x-forwarded-host
,x-forwarded-host-对于
或转发200确定
:空正文;如果地址是相对的(没有域),并且上述两个头都没有给出,则会发生这种情况
注:
- 与其他一些端点的
goto
参数所给出的地址不同,这里的地址不是uri解码的 {address}
根本不解析,它的路径不像对其他端点的调用那样规范化。即/login//foo.baz/../../cache
将调用/cache
,但/goto//foo.baz/../../../cache
将重定向到//foo.baz/../../cache
(远程主机foo.baz
路径../../cache
)
- 响应代码:
已知问题
- 在多线程上下文中清除缓存是不安全的。你可能会遇到重负下的问题。解决方案:等待修复…
- 当以单线程(默认)运行时,服务器有时会挂起。这似乎是一些浏览器不关闭套接字的问题。解决方案:以多线程模式运行服务器(
-t
选项)。 - 偶尔会抛出
brokenpipeerror
。有些浏览器会突然关闭套接字。解决方案:忽略它。
即将推出
- mt安全缓存的保存和清除
可能在某个时刻出现
- 用户数据库查找
- 密码策略
演示和来源
源代码和基于处理程序的演示脚本可以在https://github.com/aayla-secura/mixnmatchttp" rel="nofollow">https://github.com/aayla secura/mixnmatchttp