python WSGI 接口实现(1)

WSGI, 可能很多做python web 开发同学的都听过, 但是 WSGI 是什么, 用来干什么,今天我说一些自己的理解,有错误的地方或者描述不当的地方,望指正。

webserver 顾名思义 就是提供web服务, 包括静态页面和动态页面。那么当服务器接受到来自client的请求之后,他是如何和我们的web 应用进行通信的 ,这就是WSGI的作用。 接受HTTP请求 解析HTTP 发送 响应 这些比较底层的工作 就是WSGI的工作。于是 python 就有了WSGI接口 ,那些工作都交给WSGI处理, 而我们更专注用python 来实现业务层面的逻辑。

话不多说,先上一段代码 利用socket 实现一个简易的服务器

import socket 


HOST, PORT = 'localhost', 8080
listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.bind((HOST, PORT))
listen_socket.listen(20)
while 1:
    connection, address = listen_socket.accept()
    request = connection.recv(1024)
    line = request.splitlines()
    line = line[0].strip('\r\n')
    print line 
    http_response = """\
HTTP/1.1 200 OK  

    Hello, World! 
                    """
    connection.sendall(http_response) 
    connection.close()


做过web开发的同学 应该都对HTTP协议有一定的了解, 我在这里只做简单描述

当我们在浏览器输入例如  http://loalhost:5000/test 这样的url  他其实是有 protocol:host_name:port/path 这样的形式组成的

在你的浏览器能够发送 HTTP 请求之前,它需要与 Web 服务器建立一个 TCP 连接。然后会在 TCP 连接中发送 HTTP 请求,并等待服务器返回 HTTP 响应


接下来看看wsgi

# *-* coding: utf-8 *-*

import socket
import StringIO 

class WSGIServer(object):
    address_family = socket.AF_INET 
    socket_type = socket.SOCK_STREAM 
    request_queue_size = 5

    def __init__(self, address):
        self.socket = socket.socket(self.address_family, self.socket_type)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(address)
        self.socket.listen(self.request_queue_size)
        self.host, self.port = self.socket.getsockname()
        self.server_name = socket.getfqdn(self.host)

    def set_app(self, application):
        self.application = application 

    def server_forever(self):
        print "start listting"
        while 1:
            self.connection, client_address = self.socket.accept()
            print "recive a request!"
            self.request_handler()

    def request_handler(self):
        self.request_data = self.connection.recv(1024)
        self.parse_request()
        env = self.get_env()
        result = self.application(env, self.start_response)
        self.finish_response(result)

    def parse_request(self):
        request_data = self.request_data.splitlines()
        request_line = request_data[0].strip('\r\n')
        self.request_method, self.request_path, self.request_version = request_line.split()

    def get_env(self):
        env = {}
        # WSGI必要参数
        env['wsgi.version']      = (1, 0) 
        env['wsgi.url_scheme']   = 'http' 
        env['wsgi.input']        = StringIO.StringIO(self.request_data) #返回一个
        env['wsgi.errors']       = sys.stderr 
        env['wsgi.multithread']  = False 
        env['wsgi.multiprocess'] = False 
        env['wsgi.run_once']     = False 
        ### CGI 必需变量 
        env['REQUEST_METHOD']    = self.request_method    # GET 
        env['PATH_INFO']         = self.request_path      # 请求路径 
        env['SERVER_NAME']       = self.server_name       # localhost 
        env['SERVER_PORT']       = str(self.port)  # 8888 
        return env

    def start_response(self, status, response_headers):
        '''
            this function is used to set response_headers and status 
        ''' 
        self.headers = [status, response_headers]

    def finish_response(self, result):
        try:
            status, headers = self.headers
            response = 'HTTP/1.1 {status}\r\n'.format(status=status)
            for h in headers:
                response += '%s:%s\r\n'%(h)
            response += '\r\n'
            for data in result:
                print data 
                response += data 
            self.connection.sendall(response)
        finally:
            self.connection.close()

ADDRESS = ('localhost', 8888)
if __name__ == '__main__':
    import sys 
    if len(sys.argv) < 2:
        sys.exit('must give module:callable') 
    app_path = sys.argv[1]
    path, app = app_path.split(':')
    module = __import__(path)
    app = getattr(module, app)
    server = WSGIServer(ADDRESS)
    server.set_app(app)
    server.server_forever()

对于这个请求 他的工作 流程大概是这样子的:

  1. Web 框架提供一个可调用对象 application (WSGI 规范没有规定它的实现方式)。
  2. Web 服务器每次收到来自客户端的 HTTP 请求后,会唤醒可调用对象 applition。它会向该对象传递一个包含 WSGI/CGI 变量的环境变量字典 environ,以及一个可调用对象 start_response。
  3. Web 框架或应用生成 HTTP 状态码和 HTTP 响应头部,然后将它传给 start_response 函数,服务器会将其存储起来。同时,Web 框架或应用也会返回 HTTP 响应正文。
  4. 服务器将状态码、响应头部及响应正文组装成一个 HTTP 响应,然后将其传送至客户端

或者更详细一点:

  • 首先,服务器开始工作,然后会加载一个可调用对象 application,这个对象由你的 Web 框架或应用提供
  • 然后,服务器读取一个请求
  • 然后,服务器会解析这个请求
  • 然后,服务器会使用请求数据来构建一个 environ 字典
  • 然后,它会用 environ 字典及一个可调用对象 start_response 作为参数,来调用 application,并获取响应体内容。
  • 然后,服务器会使用 application 返回的响应体,和 start_response 函数设置的状态码及响应头部内容,来构建一个 HTTP 响应。
  • 最终,服务器将 HTTP 响应回送给客户端


猜你喜欢

转载自blog.csdn.net/dream_is_possible/article/details/79670774