python 下有個wsgi的封裝庫.wsgiref.python
WSGI 指的是 Web服務器網關接口(Python Web Server Gateway Interface) git
django的runserver用到了這個標準庫,學習一下。。web
涉及到的幾個模塊:HTTPServer,SocketServer,mimetools.Message(分析HTTP請求中的headers),socket(必須的),threading(用來實現ThreadingServer),select(用來實現非阻塞accept)apache
wsgi在python的web世界中是至關出名的。apache有個wsgi接口:mod_wsgi,因此只要把web框架作成這種形式的話,能夠比較方便的部署到Apache上。django
wsgiref中重要的模塊有simple_server,handlers模塊。分別實現了WSGIServer,SimpleHandler這兩個模塊。瀏覽器
繼承:服務器
Server:app
WSGIServer繼承自HTTPServer(主要是用到了get_request這個方法去驗證一下HTTP請求行。),HTTPServer繼承了TCPServerHTTPServer繼承自BaseServer。框架
Handlers:涉及兩種Handler:異步
WSGIRequestHandler繼承自BaseHTTPRequestHandler,後者繼承自StreamRequesthandler.功能是TCP請求->HTTP請求->WSGI請求。
ServerHandler繼承了wsgiref.handlers.SimpleHandler,後者繼承了本身的BaseHandler
至此,理清頭緒。
SocketServer.py中發生的事:
從tcp套接字講起,在SocketServer中使用select.select去輪詢server端口,timeout默認的是0.5s.若是發現該端口可讀了,說明有請求到來了,否則就一直循環,直到按下「」終止組合鍵「」
接着:在TCPServer中調用accept,返回響應套接字,今後,request對象的「根」產生了!
拿到這個request,首先去調用verify_request函數驗證ip是否符合要求(能夠加一個黑白名單什麼滴。。)
若是驗證經過--》調用process_request函數去處理請求。
process_request函數,對於ThreadingTCPServer或者ForkingTCPServer(混入ThreadingMixin或者ForkingMixin)來講,就是去創建新的線程或者進程,在新的線程或者進程中去執行finish_request完成這個請求
TCPServer自己沒有實現這個方法,由於TCPServer原本就是Server,爲何要去實現呢,那是handler須要作的。
接着finish_request函數:
生成一個(TCPHandler)對象,參數就是這個響應套接字地址,address,和self,這個self就是本server對象嘛。
而後就能夠去分析TCPHandler了
SocketServer.py中有一個StreamRequestHandler,給了一個模板。這個StreamRequestHandler繼承了BaseRequetHandler。
BaseRequestHandler其實沒作什麼事,在__init__方法中簡單的把參數記錄到這個handler實例中去,而後setup--》handle--》finish
在StreamRequestHandler中關掉了nagle(TCP默認會開啓擁塞控制,減小通信量,關閉後,每一個產生的包會被髮送),調用makefile,創建socket的讀寫文件描述符(rfile和wfile)。在mimetools.Message中,要直接用fp來作參數。。
handle就是要須要被繼續覆蓋下去的方法,爲何呢,誰也不知道要返回什麼東西。
下面是BaseHttpServer中發生的事。
BaseHttpServer主要兩個類,HTTPServer和BaseHTTPRequestHandler。
HTTPServer沒作什麼重要事,繼承一下,更名換姓,TCPServer就成了HTTPServer,由於Server的任務不就是一直等Request對象麼。。(傻瓜同樣的每秒兩次select...)
重點是BaseHTTPRequestHandler..由於繼承了StreamRequestHandler,因此關於接口的事就是去覆蓋handle方法:
對於HTTP協議來講,常見的版本是1.1,不常見的是1.0,很不常見的有0.9,奇葩的是大於1.1的。碰到大於1.1,不該該是正常的瀏覽器會產生的行爲吧。。
HTTP0.9很簡單:請求 command path,返回status msg 。
1.0和1.1主要區別在於1.0是會對於url發送一次tcp請求,1.1會在一個tcp連接中傳送多個HTTP請求,增長了效率。
繼續覆蓋handle方法:
BaseHTTPRequestHandler方法主要作的事就是分析請求行中的HTTP協議版本號返回414,400,505.錯誤。
並調用"do_"+command方法
值得提一下的是這個類提供了send_response,send_hander,end_header,方法。若是想要覆蓋掉自帶的Server的話,覆蓋version_string函數。
接下來就該看看WSGI了:
代碼在文件wsgiref.simple_server中
WSGIServer因爲繼承了HTTPServer,沒什麼要作的事。
重點仍在於Handler中:WSGIRequestHandler。。
WSGIRequestHandler繼承BaseHTTPRequestHandler:
可是並無去實現do_XXX這些方法,而是直接去覆蓋了handle這個BaseTCPServer爺爺給他的方法。。
WSGIRequestHandler借用了BaseHTTPRequestHandler的parse_request方法進行分析request,而後生產一個ServerHandler,像StreamRequestHandler同樣有個request_handler對象反過來引用這個BaseHTTPRequestHandler
源碼是這樣的:
handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging handler.run(self.server.get_app())
ServerHandler 就是 來實現WSGI規範的SimpleHandler類。最後一個參數其實就是os.environ字典而且增長了幾個變量SERVER_PROTOCOL,REQUEST_METHOD,PATH_INFO,QUERY_STRING,REMOTE_HOST,REMOTE_ADDR,CONTENT_TYPE,CONTENT_LENGTH
隨後就調用了這個實例的run方法。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.
註釋中提示到,WSGIRequestHandler不該該去調用socket的close方法。這是考慮到異步的狀況。
setup_environ仍繼續增長environ字典的內容:
def setup_environ(self): """Set up the environment for one request""" env = self.environ = self.os_environ.copy() self.add_cgi_vars() env['wsgi.input'] = self.get_stdin() env['wsgi.errors'] = self.get_stderr() env['wsgi.version'] = self.wsgi_version env['wsgi.run_once'] = self.wsgi_run_once env['wsgi.url_scheme'] = self.get_scheme() env['wsgi.multithread'] = self.wsgi_multithread env['wsgi.multiprocess'] = self.wsgi_multiprocess if self.wsgi_file_wrapper is not None: env['wsgi.file_wrapper'] = self.wsgi_file_wrapper if self.origin_server and self.server_software: env.setdefault('SERVER_SOFTWARE',self.server_software)
一個WSGI的application假如是這樣子的:
from wsgiref.simple_server import WSGIServer,WSGIRequestHandler def app(env,startresponse): w=startresponse('200 ok',[],) w("hello,world") return []
start_response比較典型,繼續看代碼。。:
def start_response(self, status, headers,exc_info=None): """'start_response()' callable as specified by PEP 333""" 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" self.status = status self.headers = self.headers_class(headers) return self.write
由於start_response要求參數爲status,headers(要求爲鍵值元組的列表),才能返回socket的write方法。因此必定會先返回header。而後返回body。
在startresponse簡單的判斷下參數是否合理,而後封裝爲Headers類對象這個Headers類不是
而write方法也不是簡單的wfile.write()..
根據status和headers調用_write去輸出HTTP響應行,Server,Date。
因此,在application中能夠經過調用self.headers去修改在start_response中傳入的status和headers,這並不會致使什麼問題產生。。
而application的返回值對於HTTP協議的內容也就包含headers(參數),status(參數),body(write方法)了,若是application要返回的是文件,application應該返回 self.wsgi_file_wrapper類的實例。
而且,若是application返回的是文件,在application中是不該該調用write方法的。write方法調用會致使輸出wfile
ok,WSGI分析完了。具體的如何發送文件,查看utils.FileWrapper,其實就是每次next返回8k的數據。。