前面在Flask學習-Flask基礎之WSGI中提到了WerkZeug,咱們知道,WerkZeug是一個支持WSGI協議的Server,其實還有不少其餘支持WSGI協議的Server。http://wsgi.readthedocs.io/en/latest/servers.html,這裏能夠看到有uwsgi、werkzeug.serving、wsgiref、python-fastcgi等等幾十個。wsgiref是官方給出的一個實現了WSGI標準用於演示用的簡單Python內置庫,它實現了一個簡單的WSGI Server和WSGI Application(在simple_server模塊中),主要分爲五個模塊:simple_server, util, headers, handlers, validate。 wsgiref源碼地址:https://pypi.python.org/pypi/wsgiref。經過學習Wsgiref,我相信可以更加清晰的瞭解Web框架的實現。 html
wsgiref 是 PEP 333 定義的 wsgi 規範的範例實現,裏面的功能包括了:python
因爲simple_server中已經實現了一個簡單的WSGI Server和WSGI Application,咱們只要直接運行就能夠了。git
simple_server.py:web
if __name__ == '__main__': httpd = make_server('', 8000, demo_app) sa = httpd.socket.getsockname() print "Serving HTTP on", sa[0], "port", sa[1], "..." import webbrowser webbrowser.open('http://localhost:8000/xyz?abc') httpd.handle_request() # serve one request, then exit httpd.server_close()
而後將app運行起來後,結果以下:flask
make_server('localhost', 8000, application)服務器
先查看make_server在wsgiref中的定義:數據結構
def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler): """Create a new WSGI server listening on `host` and `port` for `app`""" # -> HTTPServer.__init__ # -> TCPServer.__init__ # -> TCPServer.server_bind # -> TCPServer.socket.bind # -> TCPServer.server_activate # -> TCPServer.socket.listen server = server_class((host, port), handler_class) server.set_app(app) return server
雖然代碼只有三行,可是能夠看出生成一個 server 都須要些什麼:app
set_app
調用。另外,在註釋部分,你能夠看到那代碼背後都發生了什麼。框架
生成 server 實例時,默認的 server_class 是 WSGIServer,它是HTTPServer的子類,後者又是TCPServer的子類,TCPServer又是BaseServer的子類。因此初始化 server 時,會沿着類的繼承關係執行下去,最終,生成 server 實例的過程,實際上是最底層的 TCPServer 在初始化時,完成了對socket的bind和listen。socket
後面的 set_app 設置了 app,它會在 handler_class (默認爲WSGIRequestHandler)的handle函數中被取出來,而後交給 handler 的 run 函數運行。過程以下:
上圖能夠看出函數之間的調用關係,也能夠看出 make_server 到 使用 socket 監聽用戶請求的過程。至此,Server端已經起來了。
httpd.handle_request()
整個過程能夠看出,HTTP創建於TCP之上。
稍微難理解一點的地方是:
一、BaseServer.finish_requset()後,就到了WSGIRequestHandler初始化這一步。
二、BaseRequestHandler初始化時,會調用handle()
BaseServer
最須要注意的是在構造的時候設置了請求處理類RequestHandlerClass
。
class BaseServer:
def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.__is_shut_down = threading.Event()
self.__shutdown_request = False
先回顧一下流程,當請求走到TCPServer.handle_request,過程以下:
BaseServer
監聽到鏈接,調用self._handle_request_noblock()
處理self._handle_request_noblock()
最終找到finish_request方法finish_request
方法實例化請求處理類RequestHandlerClass
RequestHandlerClass是由調用make_server()時傳入的handler_class
def make_server(
host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server = server_class((host, port), handler_class) #WSGIServer((host,port), WSGIRequestHandler)
server.set_app(app) return server
WSGIServer的初始化最終會調用BaseServer的__init__()函數。這裏的RequestHandlerClass其實就是make_server傳入的WSGIRequestHandler。
從圖中能夠看出handler模塊中的四部分,它們實際上是四個類,從基類到子類依次爲:
最主要的實如今 BaseHandler中,其它幾個類都是在基類基礎上作了簡單的實現。BaseHandler是不能直接使用的,由於有幾個關鍵的地方沒有實現,SimpleHandler是一個可使用的簡單實現。simple_server中的 ServerHandler類就是這個模塊中SimpleHandler的子類。
在 BaseHandler中,最重要的是 run
函數:
def run(self, application): """Invoke the application""" # Note to self: don't move the close()! Asynchronous servers shouldn't # call close() from finish_response(), so if you close() anywhere but # the double-error branch here, you'll break asynchronous servers by # prematurely closing. Async servers must return from 'run()' without # closing if there might still be output to iterate over. try: self.setup_environ() self.result = application(self.environ, self.start_response) self.finish_response() except: try: self.handle_error() except: # If we get an error handling an error, just give up already! self.close() raise # ...and let the actual server figure it out.
它先設置好環境變量,再調用咱們的 demo_app
, 並經過 finish_response
將調用結果傳送給客戶端。若是處理過程遇到錯誤,轉入 handle_error
處理。
此外,BaseHandler中還包含了 WSGI 中屢次提到的 start_response,start_response 在 demo_app
中被調用,咱們看看它的實現:
def start_response(self, status, headers,exc_info=None): """'start_response()' callable as specified by PEP 333""" # M: # exc_info: # The exc_info argument, if supplied, must be a Python sys.exc_info() # tuple. This argument should be supplied by the application only if # start_response is being called by an error handler. # exc_info is the most recent exception catch in except clause # in error_output: # start_response( # self.error_status,self.error_headers[:],sys.exc_info()) # headers_sent: # when send_headers is invoked, headers_sent = True # when close is invoked, headers_sent = False if exc_info: try: if self.headers_sent: # Re-raise original exception if headers sent raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None # avoid dangling circular ref elif self.headers is not None: raise AssertionError("Headers already set!") assert type(status) is StringType,"Status must be a string" assert len(status)>=4,"Status must be at least 4 characters" assert int(status[:3]),"Status message must begin w/3-digit code" assert status[3]==" ", "Status message must have a space after code" if __debug__: for name,val in headers: assert type(name) is StringType,"Header names must be strings" assert type(val) is StringType,"Header values must be strings" assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed" # M: set status and headers self.status = status # M: # headers_class is Headers in module headers self.headers = self.headers_class(headers) return self.write
能夠看出,它先對參數進行了檢查,而後再將headers 存儲在成員變量中,這兩點 WSGI標準中都有明確說明,須要檢查參數,而且這一步只能將 headers存儲起來,不能直接發送給客戶端。也就是說,這個 start_response
尚未真正 response。
其實剛剛介紹 run
函數的時候已經提到了,真正的 response 在 finish_response
函數中:
def finish_response(self): """Send any iterable data, then close self and the iterable Subclasses intended for use in asynchronous servers will want to redefine this method, such that it sets up callbacks in the event loop to iterate over the data, and to call 'self.close()' once the response is finished. """ # M: # result_is_file: # True if 'self.result' is an instance of 'self.wsgi_file_wrapper' # finish_content: # Ensure headers and content have both been sent # close: # Close the iterable (if needed) and reset all instance vars if not self.result_is_file() or not self.sendfile(): for data in self.result: self.write(data) # send data by self.write self.finish_content() self.close()
另一個須要注意的地方是錯誤處理,在 run
函數中經過 try/except
捕獲錯誤,錯誤處理使用了 handle_error
函數,WSGI中提到,start_response
函數的第三個參數 exc_info
會在錯誤處理的時候使用,咱們來看看它是如何被使用的:
def handle_error(self): """Log current error, and send error output to client if possible""" self.log_exception(sys.exc_info()) if not self.headers_sent: self.result = self.error_output(self.environ, self.start_response) self.finish_response() # XXX else: attempt advanced recovery techniques for HTML or text? def error_output(self, environ, start_response): """WSGI mini-app to create error output By default, this just uses the 'error_status', 'error_headers', and 'error_body' attributes to generate an output page. It can be overridden in a subclass to dynamically generate diagnostics, choose an appropriate message for the user's preferred language, etc. Note, however, that it's not recommended from a security perspective to spit out diagnostics to any old user; ideally, you should have to do something special to enable diagnostic output, which is why we don't include any here! """ # M: # sys.exc_info(): # Return information about the most recent exception caught by an except # clause in the current stack frame or in an older stack frame. start_response(self.error_status,self.error_headers[:],sys.exc_info()) return [self.error_body]
看到了吧,handle_error
又調用了 error_output
,後者調用 start_response
,並將其第三個參數設置爲 sys.exc_info()
,這一點在 WSGI 中也有說明。
好了,這一部分咱們就介紹到這裏,再也不深刻過多的細節,畢竟源代碼仍是要本身親自閱讀的。剩下的三個部分不是核心問題,就是一些數據結構和工具函數,咱們簡單說一下。
這個模塊是對HTTP 響應部分的頭部設立的數據結構,實現了一個相似Python 中 dict的數據結構。能夠看出,它實現了一些函數來支持一些運算符,例如 __len__
, __setitem__
, __getitem__
, __delitem__
, __str__
, 另外,還實現了 dict 操做中的 get
, keys
, values
函數。
這個模塊主要就是一些有用的函數,用於處理URL, 環境變量。
這個模塊主要是檢查你對WSGI的實現,是否知足標準,包含三個部分:
validator 調用後面兩個部分來完成驗證工做,能夠看出Check部分對WSGI中規定的各個部分進行了檢查。