Technology Sharing

WSGI Server Tutorial: `start_response` Method Explained

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Python WSGI Server Tutorial:start_response Method Analysis

In this article, we will take a closer look at a WSGI server. start_response Method. This method is responsible for processing the HTTP response status code and response header, and returns awrite Function to send the response data. We will explain line by line how this method works and provide some background to help understand its functionality.

background knowledge

WSGI (Web Server Gateway Interface) is a standard interface for connecting a web server to a web application or framework. Through WSGI, the server and the application can communicate and process HTTP requests and responses.

When implementing a WSGI server,start_response The method is a key part, which is called by the WSGI application to set the HTTP response status and response headers and return a function for writing the response data.

start_response Method Implementation

The following is a typical start_response Method implementation:

def start_response(status, response_headers, exc_info=None):
    if exc_info:
        try:
            if headers_sent:
                reraise(*exc_info)
        finally:
            exc_info = None
    elif headers_set:
        raise AssertionError("Headers already set")
    headers_set[:] = [status, response_headers]
    return write
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Step by step explanation

  1. deal with exc_info parameter
if exc_info:
    try:
        if headers_sent:
            reraise(*exc_info)
    finally:
        exc_info = None
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • exc_info The argument usually contains an exception tuple of the form(type, value, traceback), used for exception handling. If exc_info Not forNone, it means there is an exception that needs to be handled.
  • try In-block inspectionheaders_sent Whether the response header has been sent. If the response header has been sent, callreraise(*exc_info) Re-raise the exception.
  • finally The block ensures that after handling the exception,exc_info Set asNoneto avoid duplicate processing.
  1. Check if the response header has been set
elif headers_set:
    raise AssertionError("Headers already set")
  • 1
  • 2
  • if headers_set The list already contains a value (that is, the response header has been set), triggeringAssertionError This check ensures thatstart_response Can only be called once to set the response header.
  1. Set response status and response headers
headers_set[:] = [status, response_headers]
  • 1
  • Using slice assignment status andresponse_headers Assign toheaders_set The purpose of this is to preserveheaders_set , but update its content.
  • status Is a string representing the HTTP response status code and message, for example"200 OK"
  • response_headers is a list of tuples, each tuple represents a key-value pair of a response header, for example[("Content-Type", "text/html"), ("Content-Length", "123")]
  1. return write function
return write
  • 1
  • start_response The method returns awrite Function. This function is called by the WSGI application to send response data.

Usage Examples

The following is a complete example showing how to use start_response Method to handle WSGI response:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.headers_set = []
        self.headers_sent = []

        def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if self.headers_sent:
                        raise exc_info[1]
                finally:
                    exc_info = None
            elif self.headers_set:
                raise AssertionError("Headers already set")
            self.headers_set[:] = [status, response_headers]
            return self.write

        def write(data):
            assert self.headers_set, "write() before start_response"
            if not self.headers_sent:
                status, response_headers = self.headers_sent[:] = self.headers_set
                try:
                    code, msg = status.split(None, 1)
                except ValueError:
                    code, msg = status, ""
                code = int(code)
                self.send_response(code, msg)
                header_keys = set()
                for key, value in response_headers:
                    self.send_header(key, value)
                    key = key.lower()
                    header_keys.add(key)
                if not (
                    "content-length" in header_keys
                    or self.environ["REQUEST_METHOD"] == "HEAD"
                    or code < 200
                    or code in (204, 304)
                ):
                    self.close_connection = True
                    self.send_header("Connection", "close")
                if "server" not in header_keys:
                    self.send_header("Server", self.version_string())
                if "date" not in header_keys:
                    self.send_header("Date", self.date_time_string())
                self.end_headers()

            assert isinstance(data, bytes), "applications must write bytes"
            self.wfile.write(data)
            self.wfile.flush()

        self.write = write
        self.environ = self.make_environ()

        try:
            result = self.server.app(self.environ, start_response)
            try:
                for data in result:
                    write(data)
                if not self.headers_sent:
                    write(b"")
            finally:
                if hasattr(result, "close"):
                    result.close()
        except Exception as e:
            self.send_error(500, str(e))

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        print("Server started at {}:{}".format(HOST, PORT))
        server.serve_forever()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

Summarize

Through this tutorial, we have analyzed in detail a WSGI server start_response method, explains how it handles the status code and response header of the HTTP response, and returns a function for writing the response data. Understanding these contents will help you better grasp the WSGI specification and implement a custom WSGI server. I hope this tutorial is helpful to you. For more detailed information and examples, please refer toOfficial Documentation