• wsgiref源码解析


    wsgirefPEP 333定义的wsgi规范的范例实现,里面的功能包括了:

    • wsgi的环境变量
    • 应答头部的处理
    • 实现简单的HTTP服务器
    • 简单的对程序端和服务器端校验函数

    我们先看一个简单的代码实例,然后跟着例子去理解源码:
    app.py

    #encoding:utf-8
    # __author__ = 'donghao'
    # __time__ = 2019/3/29 14:17
    
    def hello_world_app(environ, start_response):
        from io import StringIO
        stdout = StringIO()
        start_response("200 OK", [('Content-Type','text/html; charset=utf-8')])
        return ['<h1>hello world</h1>'.encode('utf-8')]
    

    server.py

    from wsgiref.simple_server import make_server
    httpd = make_server('', 8000, hello_world_app)
    print("Serving on port 8000...")
    httpd.serve_forever()
    
    #浏览器
    """
    Content-Length: 20
    Content-Type: text/html; charset=utf-8
    Date: Fri, 29 Mar 2019 08:08:47 GMT
    Server: WSGIServer/0.2 CPython/3.6.4
    """
    #server终端
    """
    Serving on port 8000...
    127.0.0.1 - - [29/Mar/2019 16:19:12] "GET / HTTP/1.1" 200 20
    """
    

    源码分析:

    wsgiref
    |-- handlers.py # 核心代码,负责 wsgi 程序的处理
    |-- headers.py # 头部处理的代码
    |-- init.py #
    |-- simple_server.py # 简单的 wsgi HTTP 服务器实现
    |-- util.py # 帮助函数
    `-- validate.py # wsgi 格式检查和校验
    主要的代码结构如下图所示:

    在这里插入图片描述
    simple_server.py
    我们先看一下make_server是怎么启动一个wsgi服务器的:

    def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):
        server = server_class((host, port), handler_class)
        server.set_app(app)
        return server
    

    这个函数做的事情就是:监听在本地的端口上,接受来自客户端的请求,通过WSGIServerWSGIRequestHandler处理后,把请求交给程序的的可调用对象app,然后返回app的结果给客户端。

    这里有两个重要的类:WSGIServer和WSGIRequestHandler。下面分别看一下它们的代码和执行的功能。

    WSGIServer

    #WSGIServer
    class WSGIServer(HTTPServer):
    
        """BaseHTTPServer that implements the Python WSGI protocol"""
    
        application = None
    
        def server_bind(self):
            """Override server_bind to store the server name."""
            HTTPServer.server_bind(self)
            self.setup_environ()
    
        def setup_environ(self):
            # Set up base environment
            env = self.base_environ = {}
            env['SERVER_NAME'] = self.server_name
            env['GATEWAY_INTERFACE'] = 'CGI/1.1'
            env['SERVER_PORT'] = str(self.server_port)
            env['REMOTE_HOST']=''
            env['CONTENT_LENGTH']=''
            env['SCRIPT_NAME'] = ''
    
        def get_app(self):
            return self.application
    
        def set_app(self,application):
            self.application = application
    

    WSGIServer在原来的HTTPServer上面封装了一层,在原来的HTTPServer的基础上又额外做了下面的事情:

    • 覆写原来的server_bind函数,添加初始化environ变量的动作
    • 添加了处理满足wsgi的app函数set_appget_app

    WSGIRequestHandler

    # WSGIRequestHandler
    class WSGIRequestHandler(BaseHTTPRequestHandler):
    
        server_version = "WSGIServer/" + __version__
    
        def get_environ(self):
            env = self.server.base_environ.copy()
            env['SERVER_PROTOCOL'] = self.request_version
            env['REQUEST_METHOD'] = self.command
            if '?' in self.path:
                path,query = self.path.split('?',1)
            else:
                path,query = self.path,''
    
            env['PATH_INFO'] = urllib.unquote(path)
            env['QUERY_STRING'] = query
    
            host = self.address_string()
            if host != self.client_address[0]:
                env['REMOTE_HOST'] = host
            env['REMOTE_ADDR'] = self.client_address[0]
    
            if self.headers.typeheader is None:
                env['CONTENT_TYPE'] = self.headers.type
            else:
                env['CONTENT_TYPE'] = self.headers.typeheader
    
            length = self.headers.getheader('content-length')
            if length:
                env['CONTENT_LENGTH'] = length
    
            for h in self.headers.headers:
                k,v = h.split(':',1)
                k=k.replace('-','_').upper(); v=v.strip()
                if k in env:
                    continue                    # skip content length, type,etc.
                if 'HTTP_'+k in env:
                    env['HTTP_'+k] += ','+v     # comma-separate multiple headers
                else:
                    env['HTTP_'+k] = v
            return env
    
        def get_stderr(self):
            return sys.stderr
    
        def handle(self):
            """Handle a single HTTP request"""
    
            self.raw_requestline = self.rfile.readline()
            if not self.parse_request(): # An error code has been sent, just exit
                return
    
            handler = ServerHandler(
                self.rfile, self.wfile, self.get_stderr(), self.get_environ()
            )
            handler.request_handler = self      # backpointer for logging
            handler.run(self.server.get_app())
    

    这个类从名字就能知道它的功能 - 处理客户端的HTTP请求,它也是在原来处理http请求的BaseHTTPRequestHandler类上添加了wsgi规范相关的内容。

    get_environ:解析环境变量
    handle:处理请求,把封装的环境变量交给ServerHandler,然后由ServerHandler调用wsgi appServerHandler类会在下面介绍。

    handler.py
    这个文件主要是wsgi server的处理过程,定义start_response,调用wsgi app,处理content-length等等。

    在这里插入图片描述


    一条HTTP请求的旅程
    服务器端启动服务,等到客户端输入curl -i http://localhost:8000/命令,摁下回车键,看到终端上的输出,整个过程中,wsgi的服务器端发生了什么呢?

    1. 服务器程序创建socket,并监听在特定的端口,等待客户端的连接
    2. 客户端发送http请求
    3. socket服务器读取请求的数据,交给http服务器
    4. http server根据http的规范解析请求,然后把请求交给WSGIServer
    5. WSGIServer把客户端的信息存放在环境变量里,然后交给绑定的handler handler理请求
    6. HTTPHandler解析请求,把方法,路径等放在environ,然后WSGIRequestHandler把服务器端的信息也放到environ里
    7. WSGIRequestHandler调用绑定的wsgi ServerHandler,把上面包含了服务器信息,客户端信息,本次请求信息得environ传递过去
    8. wsgi ServerHandler调用注册的wsgi app,把环境和start_response传递过去
    9. wsgi app将响应头,状态,身体回传给wsgi处理程序
    10. 然后处理器逐层传递,最后把这些信息通过socket发送到客户端
    11. 客户端的程序接到应答,解析应答,并把结果打印出来
      ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019032916561883.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMjM5NTIw,size_16,color_FFFFFF,t_70 =2000x800)
      自己画的图,虽有点丑,回顾还是很有用。
      在这里插入图片描述
  • 相关阅读:
    用 Flash 制作留言板
    解决IIS5 HTTP500内部错误
    人体塑造教程+源文件+录象教程
    sql溢出攻击
    美化windows xp 完全教程
    简单手绘背景绘画过程树!
    今天把伊苏6 玩潼关了!庆祝一下!发点图给大家欣赏。
    Rundll.exe 命令详解
    终端服务的日志监控
    用Visual C#实现局域网点对点通讯
  • 原文地址:https://www.cnblogs.com/donghaoblogs/p/10622514.html
Copyright © 2020-2023  润新知