What WSGI must know

The python video tutorial section introduces what is WSGI.

I have written python web for a few years, but I still don’t know what WSGI is, is there a lot of people. It's normal, because as a developer, you rarely need to understand what wsgi is and be able to make a website.

But if you want to write a web framework for fun, you have to learn about wsgi.

To recap, when we use python for web development, we generally develop based on a certain web framework, such as django or flask. After the business development is completed, it must be deployed to a server to provide external access.

At this time, if you search the Internet, they will tell you that you need to use gunicorn or uwsgi to deploy. So what are gunicorn and uwsgi?

Look at this picture, you will understand, I found the picture from the Internet
Insert picture description here

The role played by uwsgi or gunicorn here is the role of a web server. The server here is a software-level server for processing HTTP requests sent by the browser and returning the response results to the front end. The main task of the web framework is to process the business logic to generate the results to the web server, and then the web server returns to the browser.

The communication between the web framework and the web server needs to follow a set of specifications, this specification is WSGI.

Why is there such a set of specifications? The standard is to unify the standard and make it easy for everyone to use

Imagine that our mobile phone charging interface is now Type-c. Type-c is a specification. Mobile phone manufacturers produce mobile phones according to this specification. Charger manufacturers produce chargers according to Type-c specifications. Mobile phones from different manufacturers It can be used with chargers from different manufacturers. However, Apple has established its own set of specifications, which finally caused the Android charger to fail to charge Apple.

So how to write an application (framework) program and server that conforms to the WSGI specification?

As shown in the figure above, on the left is the web server, and on the right is the web framework, or application.

Application
WSGI stipulates that the application must be a callable object (the callable object can be a function, a class, or an instance object that implements __call__), and must accept two parameters, the return value of the object Must be an iterable object.

We can write an example of the simplest application

HELLO_WORLD = b"Hello world!\n"def application(environ, start_response):

    status = '200 OK'

    response_headers = [('Content-type', 'text/plain')]

    start_response(status, response_headers)    return [HELLO_WORLD]复制代码

application is a function, it must be a callable object, and then receives two parameters, the two parameters are: environ and start_response

environ is a dictionary that stores all content related to the HTTP request, such as headers, request parameters, etc.
start_response is a function passed by the WSGI server to pass the response header and status code to the server.
Calling the start_response function is responsible for passing the response header and status code to the server, and the response body is returned to the server by the application function. A complete http response is provided by these two functions.

But any web framework that implements wsgi will have such a callable object

What the server
WSGI server does is each time it receives an HTTP request, constructs an environ object, then calls the application object, and finally returns the HTTP Response to the browser.

The following is a complete wsgi server code

import socketimport sysfrom io import StringIOclass WSGIServer(object):

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 1

 

    def __init__(self, server_address):

        # Create a listening socket

        self.listen_socket = listen_socket = socket.socket(

            self.address_family,

            self.socket_type

        )        # Allow to reuse the same address

        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        # Bind

        listen_socket.bind(server_address)        # Activate

        listen_socket.listen(self.request_queue_size)        # Get server host name and port

        host, port = self.listen_socket.getsockname()[:2]

        self.server_name = socket.getfqdn(host)

        self.server_port = port        # Return headers set by Web framework/Web application

        self.headers_set = []    def set_app(self, application):

        self.application = application    def serve_forever(self):

        listen_socket = self.listen_socket        while True:            # New client connection

            self.client_connection, client_address = listen_socket.accept()            # Handle one request and close the client connection. Then

            # loop over to wait for another client connection

            self.handle_one_request()    def handle_one_request(self):

        self.request_data = request_data = self.client_connection.recv(1024)        # Print formatted request data a la 'curl -v'

        print(''.join(            '< {line}\n'.format(line=line)            for line in request_data.splitlines()

        ))

        self.parse_request(request_data)        # Construct environment dictionary using request data

        env = self.get_environ()        # It's time to call our application callable and get

        # back a result that will become HTTP response body

        result = self.application(env, self.start_response)        # Construct a response and send it back to the client

        self.finish_response(result)    def parse_request(self, text):

        request_line = text.splitlines()[0]

        request_line = request_line.rstrip('\r\n')        # Break down the request line into components

        (self.request_method,  # GET

         self.path,  # /hello

         self.request_version  # HTTP/1.1

         ) = request_line.split()    def get_environ(self):

        env = {
    
    }        # The following code snippet does not follow PEP8 conventions

        # but it's formatted the way it is for demonstration purposes

        # to emphasize the required variables and their values

        #

        # Required WSGI variables

        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

        # Required CGI variables

        env['REQUEST_METHOD'] = self.request_method  # GET

        env['PATH_INFO'] = self.path  # /hello

        env['SERVER_NAME'] = self.server_name  # localhost

        env['SERVER_PORT'] = str(self.server_port)  # 8888

        return env    def start_response(self, status, response_headers, exc_info=None):

        # Add necessary server headers

        server_headers = [

            ('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),

            ('Server', 'WSGIServer 0.2'),

        ]

        self.headers_set = [status, response_headers + server_headers]        # To adhere to WSGI specification the start_response must return

        # a 'write' callable. We simplicity's sake we'll ignore that detail

        # for now.

        # return self.finish_response

 

    def finish_response(self, result):

        try:

            status, response_headers = self.headers_set

            response = 'HTTP/1.1 {status}\r\n'.format(status=status)            for header in response_headers:

                response += '{0}: {1}\r\n'.format(*header)

            response += '\r\n'

            for data in result:

                response += data            # Print formatted response data a la 'curl -v'

            print(''.join(                '> {line}\n'.format(line=line)                for line in response.splitlines()

            ))

            self.client_connection.sendall(response)        finally:

            self.client_connection.close()

 

 

SERVER_ADDRESS = (HOST, PORT) = 'localhost', 8080def make_server(server_address, application):

    server = WSGIServer(server_address)

    server.set_app(application)    return serverif __name__ == '__main__':

    httpd = make_server(SERVER_ADDRESS, application)

    print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))

    httpd.serve_forever()复制代码

Of course, if you just write a server for development environment, you don't need to bother to make your own wheels, because the built-in python module provides the function of wsgi server.

from wsgiref.simple_server import make_server

srv = make_server('localhost', 8080, application)

srv.serve_forever()复制代码

As long as 3 lines of code can provide wsgi server, is it super convenient? Finally, I will visit and test the effect of a request initiated by the browser

Insert picture description here

The above is the introduction of wsgi. If you have a deep understanding of wsgi, you can be familiar with PEP333.
This article comes from the python video tutorial section of php Chinese website : https://www.php.cn/course/list/30.html

Guess you like

Origin blog.csdn.net/Anna_xuan/article/details/109745080