模块化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 defaultdo_defaultbasehttprequesthandler但您的类可能要重写它。

模板页是参数化的响应体。模板指定模板 要与一起使用的页,并提供要与一起使用的参数和值的字典 模板。每个参数值还可以包含动态参数 给了来自模板的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/bardo-foo,则 选中时,将是/foo;如果使用do_default为空(')。
  • sub:端点路径的其余部分,不带前导符/,例如barfoo/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_debugwithep,一份myhandler.\u endpoints['debug']['sub']ep.root将为"debug",ep.sub将为"sub",ep.args将为"../foo//bar//baz"。self.command将给出http方法。
  • 请求/debug/foo/./bar将规范化/debug/bar的路径(因为/debugraw_argsfalse)使用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确定:验证成功;会话设置cookie
      • 401未经授权:用户名或密码无效;
      • 302 found:位置是通过goto参数请求的
    • 注:
      • 重新启动时服务器端会忘记会话
      • Cookie发出时带有httponly标志,如果通过SSL发出时也带有secure标志
  • 获取/注销:从浏览器和服务器中清除会话cookie
    • 支持的URL参数:
      • 转到:重定向到此URL
    • 响应代码:
      • 200正常:空正文
      • 302 found:位置是通过goto参数请求的
  • get post/changepwd:更改给定用户名的密码
    • 支持的URL参数:
      • 转到:重定向到此URL
    • 必需的正文或URL参数:
      • 用户名:用户名(duh)
      • 密码:当前密码
      • 新密码:新密码(duh)
    • 响应代码:
      • 200确定:成功;密码已更改,当前 会话无效,并设置新的会话cookie
      • 401未经授权:用户名或密码无效
      • 302 found:位置是通过goto参数请求的

cachingtprequesthandler

允许将内容保存为命名页以供以后的请求使用,或将编码的(URL或Base64)请求内容显示为响应页。

  • post/echo:呈现请求的内容
    • 支持的URL参数:
      • 数据:要呈现的页的编码内容(必需)
      • 键入:呈现页面的内容类型(默认为文本/纯文本)
    • 支持的格式:
      • 应用程序/json使用base64编码数据
      • 应用程序/x-www-form-url encoded(使用url编码的数据)
    • 响应代码:
      • 200好:正文和内容类型都是按要求的
      • 400错误请求:无法解码数据或查找数据参数
  • post/cache/{name}:临时保存请求的内容(仅在内存中)
    • 支持的URL参数和格式与post/echo
    • 响应代码:
      • 204无内容:页已缓存
      • 500服务器错误:已达到最大缓存内存,或页面{name}已缓存
    • 注:
      • 保存后,即使页面已从内存中清除(请参见/cache/clear
  • 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/之后的地址;如果没有给定域(即地址不是以架构:////开头),则从refereroriginx-forwarded-hostx-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

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java为什么Camel Spring 2.20.2会导致NIST漏洞CVE20169878和CVE20175929   javascript如何在Lodash中获取数组索引。每个   如何在java中欺骗ip   选择的java DropdownChoice与模型值不同,在ajax更新时更改   用于Java库ant构建的GnuPG问题/错误   java Atlas Mapper:使用Atlas Mapping文档为给定负载执行映射   java Paypal Broadleaf集成   java Meteor客户端函数,然后是服务器端响应   JavaJPA2在JavaSE中使用EntityManager有几个问题   java是否将时间戳格式的值更改为其他时间戳格式?   java为什么IF语句不能处理四舍五入的浮点和双精度浮点?   java无法访问handle事件中的按钮/文本区域   java Eclipse将数据插入MySQL时出现while循环未指定值错误   调试是java类库的启用调试/编译版本仍然可用   异步java。lang.ClassCastException:无法强制转换为组织。springframework。util。同时发生的可听未来   打印总是选择默认打印机,而不是指定的JAVA打印机   在Java中实现多重继承的对象   java如何从具有特定联系人的其他应用程序打开Whatsapp?   unicode字符串的java gson序列化不起作用   java如何从Android向flask服务器发送带有参数的post请求?