Basic Knowledge python knowledge
- Iterator and generator
- Advanced usage of functions: nesting functions, passing as parameters, etc.
- Understanding decorator will be very helpful in understanding WSGI.
- The callable concept of Python
- The concept of Classmethod and Staticmethod
- Fundamentals of Web Programming
HTTP Basics
The most basic concept for a Web application is to send a request to the client and receive a server-side response (response).
The following is a simple HTTP request:
GET /Index.html HTTP/1.1\r\n Connection: Keep-Alive\r\n Accept: */*\r\n User-Agent: Sample Application\r\n Host: www.microsoft.com\r\n\r\n
The content includes the method, URL, protocol version, and header information. The HTTP response (excluding data) might be something like this:
HTTP/1.1 200 OK Server: Microsoft-IIS/5.0\r\n Content-Location: http://www.microsoft.com/default.htm\r\n Date: Tue, 25 Jun 2002 19:33:18 GMT\r\n Content-Type: text/html\r\n Accept-Ranges: bytes\r\n Last-Modified: Mon, 24 Jun 2002 20:27:23 GMT\r\n Content-Length: 26812\r\n
In actual production, the Python program is placed on the server's HTTP server (such as Apache, Nginx, etc.). The question now is how does the server program pass the received request to Python, and how does it switch between the network's data stream and the python structure? This is what WSGI does: a set of specifications on the terminal and server side, or a unified interface.
WSGI
Let's take a look at what the Python program for HTTP needs to care about:
- Request
- Method of Request
- URL of the requested address
- Content of the request
- Header headers of the request
- Requested environment information
- Response
- Status Code Status_code
- Data in response
- The head of the response
The task of WSGI (Web Server Gateway Interface) is to simply and amicably pass the above data between the HTTP Server and the Python program. It is a standard that is defined in Pep 333. HTTP server and Python programs are required to comply with certain specifications to achieve the standard of the agreed content, in order to work properly.
Application-Side
WSGI specifies that each Python program (application) must be a callable object (a __call__
method or class that implements the function), accepts two parameters environ
(WSGI environment information), and (the function that start_response
starts responding to the request), and returns Iterable. A few notes:
environ
and start_response
provided by HTTP server and implemented
environ
A variable is a dictionary that contains information about the environment
Application
Called internally before returningstart_response
start_response
Also a callable that accepts two required parameters status
(HTTP status) and response_headers
(the header of the response message)
- Callable object to return a value, which is iterative.
Having said so many concepts, take a look at the implementation of the Code:
# 1. Callable object is a function def application (environ, start_response): Response_body = ' The request method was%s '% environ[' Request_metho D '] # HTTP response code and message status = ' OK ' # The header of the answer is a list, each pair of key values must be a tuple. Response_headers = [(' Content-type ', ' Text/plain '), (' Content-length ', str (len (response_body)))] # Call the server program provided by Start_response, fill in two parameters Start_response (status, response_headers) # return must be iterable return [response_body] # 2. The callable object is a class Appclass: "" The callable object here is the Appclass class, which is called to produce the result that can be iterated. Use a method similar to: for result in Appclass (env, Start_response): do_somthing (Result) "" "Def __init__ (self, environ, start_resp Onse): Self.environ = Environ Self.start = Start_response def __iter__ (self): status = ' OK ' Response_headers = [(' Content-type ', ' Text/plain ')] Self.start (status, response_headers) yield "Hello W orld!\n "# 3. The callable object is an instance of class Appclass: "" The callable object here is an instance of Appclass, using a method similar to: app = Appclass () for REsult in App (environ, start_response): do_somthing (Result) "" "Def __init__ (self): Pass def __call__ (self, Environ, start_response): status = ' OK ' response_headers = [(' Content-type ', ' Text/plain ')] Self . Start (status, Response_headers) yield "Hello world!\n"
Server program Side
As already mentioned above, the standard to be able to implement exactly, must require both the terminal and the server side to abide by. As mentioned above, envrion
and start_response
both are provided on the server side. The following is a look at the server side to fulfill the obligations.
- Prepare
environ
Parameters
- Defining
start_response
Functions
- Invokes a callable object on the terminal
PEP 333 gives a simple implementation of a WSGI server, and I've simplified it--removing some exception handling and judgment and adding a bit of comment:
Import OS, Sysdef run_with_cgi (application): # application is the terminal callable Object # Prepare the Environ parameter, this is a dictionary, inside the content is an HTTP request environment variable en Viron = Dict (Os.environ.items ()) environ[' wsgi.input '] = Sys.stdin environ[' wsgi.errors '] = Sys.stderr environ[' wsgi.version ' = (1, 0) environ[' wsgi.multithread '] = False environ[' wsgi.multiprocess '] = True environ[' wsgi.run_once ' = True environ[' wsgi.url_scheme '] = ' http ' headers_set = [] Headers_sent = []# outputs the result of the answer to the terminal def write: sys.stdout.write (data) Sys.stdout.flush () # implements the Start_response function, which is transmitted by the program end. Status and Response_headers parameters, # set state and head def start_response (status, Response_headers, Exc_info=none): Headers_se t[:] = [status, Response_headers] return write# invokes the client's callable object, passing the prepared parameters past result = Application (environ, start_respon SE) # Processes The resulting results, which simply output the results to standard output. Try:for data in result:if data: # don ' t send headers until body appears Write (data) finally:if hasattr (result, ' close '): Result.close ()
Middle Middleware
Some programs may be between the server side and the terminal: for a server program, it is an application, and for an application it is a server program. This is the middle-tier middleware. Middleware is transparent to the server program and application, it is like a proxy/pipeline, the received request for some processing, and then passed, passed to the client program, and finally the program's client processing results returned. As shown in the following:
Related: Http://stackoverflow.com/questions/1303118/looking-for-a-diagram-to-explain-wsgi
http://www.python.org/dev/peps/pep-0333/
A picture wins thousands of words, a picture wins thousands of words
Middleware has done two things:
- Be called by the server program (possibly other middleware), return the result back
- Invoke the application (possibly other middleware) and pass the arguments past
PEP 333 shows the possible usage scenarios for middleware:
- The request is given to a different client program (URL routing) based on the URL
- Allowing multiple client programs to run concurrently with the/web framework is to pass the same request to multiple programs.
- Load balancing and remoting: Transferring requests over the network
- Filtering Processing of responses
So what is the simple implementation of middleware? The following code implements the middleware of a simple URL routing:
class Router(object): def __init__(self): self.path_info = {} def route(self, environ, start_response): application = self.path_info[environ[‘PATH_INFO‘]] return application(environ, start_response) def __call__(self, path): def wrapper(application): self.path_info[path] = application return wrapperrouter = Router()
How to use it inside the program?
#here is the application@router(‘/hello‘)#调用 route 实例,把函数注册到 paht_info 字典def hello(environ, start_response): status = ‘200 OK‘ output = ‘Hello‘ response_headers = [(‘Content-type‘, ‘text/plain‘), (‘Content-Length‘, str(len(output)))] write = start_response(status, response_headers) return [output]@router(‘/world‘)def world(environ, start_response): status = ‘200 OK‘ output = ‘World!‘ response_headers = [(‘Content-type‘, ‘text/plain‘), (‘Content-Length‘, str(len(output)))] write = start_response(status, response_headers) return [output]#here run the applicationresult = router.route(environ, start_response)for value in result: write(value)
Note: The above code is from this blog.
Learn more?
For ordinary developers, understanding the above knowledge is sufficient, and does not need to master every detail.
Only authors of Web servers and programming frameworks need to know every detail and corner case of the WSGI design. You don ' t need to understand every detail of WSGI just to install a WSGI application or to write a Web application using a N Existing framework.
If you want more, go to see PEP333, and the documentation has more knowledge:
- Error handling
- What are the values that the Environ variable contains, and what does it mean?
- Details of inputs and outputs
- More Specifications for Start_response
- Content-length and other head specifications
- Caching and text flow
- Unicode and multi-lingual processing
- ......
Resources
- Official Document PEP333
- Getting Started with WSGI
- A very concise and easy-to-understand WSGI introduction
- Wsgiref: Official WSGI implementations, including client and server side
(reprint-Learn) Python Wsgi introduction