SimpleHTTPServer模組提供了建立一個http服務的例子。Python web領域裡卻很少這麼做,而是使用了另外一個協議 — WSGI協議。Python的wisiref模組提供了demo_app,來演示如何使用wsgi協議。
1 2 3 4 5 6 7 8 9 10 |
def demo_app(environ,start_response): from StringIO import StringIO stdout = StringIO() print >>stdout, "Hello world!" print >>stdout h = environ.items(); h.sort() for k,v in h: print >>stdout, k,'=', repr(v) start_response("200 OK", [('Content-Type','text/plain')]) return [stdout.getvalue()] |
demo_app即是一個標準的wsgi app。它接受兩個引數,一個包含cgi伺服器的環境變數,另外一個引數是一個函式,這個函式也接受兩個函式,一個是http狀態,另外是http協議的header資訊。最後該app返回一個可迭代物件,這個物件即傳送給客戶端的body內容。demo_app有一些對StringIO的操作,這些都是把environ進行格式化輸出。
Python的web框架,都是一個wsgi app。通過自己構造wsgi應用,很容易寫出一個框架的骨架。python定義了wsgi,讓web框架幾乎可以大一統了。下面就來分析,python是如何實現這個協議的。
WSGIServer
python使用WSGIServer和WSGIRequestHandler構建wsgi協議的服務。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class WSGIServer(HTTPServer): application = None def server_bind(self): HTTPServer.server_bind(self) self.setup_environ() def setup_environ(self): # 甚至環境變數 env = self.base_environ = {} env['SERVER_NAME'] = self.server_name env['GATEWAY_INTERFACE'] = 'CGI/1.1' env['SERVER_PORT'] = str(self.server_port) env['REMOTE_HOST']='' env['CONTENT_LENGTH']='' env['SCRIPT_NAME'] = '' def get_app(self): return self.application def set_app(self,application): self.application = application |
WSGIServer繼承HTTPServer,重寫了server_bind彷彿,設定了一些專用的環境變數。比較簡單,我們也知道,Server只是處理socket連線相關的邏輯,RequestHandler才是處理客戶端請求邏輯。
WSGIRequestHandler
WSGIRequestHandler 也不復雜,只有3個方法,get_environ用來設定並返回環境變數的字典,get_stderr用於獲取標準錯誤輸出。handle則是重寫基類BaseRequestHandler的方法。前文我們也提到,handle用於不同協議處理客戶端的入口。
handler
1 2 3 4 5 6 7 8 9 10 |
def handle(self): self.raw_requestline = self.rfile.readline() if not self.parse_request(): return handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self handler.run(self.server.get_app()) |
handle方法和BaseHTTPRequestHanler的handle方法所做的類似,解析驗證客戶端的http的request是否合法。不同的在於,此時會繫結一個ServerHandler的例項物件,並把緩衝可讀可寫檔案控制程式碼,環境變數等傳入該類。同時呼叫這個物件的run方法。其實,我們之前定義的app,恰恰就是傳給run方法,通過run方法的包裝,實現wsgi協議的通訊。
BaseHandler 和 ServerHandler
ServerHandler 來自wsgiref的handlers模組,它繼承 BaseHandler類。又一個帶base類。BaseHandler主要用於操作WSGI app。run方法就是在該類定義的。
1 2 3 4 5 6 7 8 9 10 11 |
def run(self, application): try: self.setup_environ() self.result = application(self.environ, self.start_response) self.finish_response() except: try: self.handle_error() except: self.close() raise |
run方法最重要的就是呼叫自定義的wsgi app,並把在finish_reponse方法中把結果send給客戶端。
finish_response
1 2 3 4 5 6 7 8 |
def finish_response(self): try: if not self.result_is_file() or not self.sendfile(): for data in self.result: self.write(data) self.finish_content() finally: self.close() |
把可迭代物件資料返回。通過write方法把資料寫入緩衝可寫檔案,然後傳送給客戶端。
send_preamble
finish_response返回http的body是一方面,其中還需要返回http的headers。這個操作分別在wirte方法和finish_content中,它們都呼叫了send_headers方法,send_headers方法由通過呼叫send_preamble構造header資料,並最終通過_write 方法寫入到緩衝可寫檔案中。完成伺服器對客戶端的響應。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def send_preamble(self): if self.origin_server: if self.client_is_modern(): self._write('HTTP/%s %s\r\n' % (self.http_version,self.status)) if 'Date' not in self.headers: self._write( 'Date: %s\r\n' % format_date_time(time.time()) ) if self.server_software and 'Server' not in self.headers: self._write('Server: %s\r\n' % self.server_software) else: self._write('Status: %s\r\n' % self.status) |
wsgiref模組還包含了另外幾個子模組,上面的的內容主要來自 simple_server 和 handler模組,此外還有headers喝validata模組,用於包裝headers和做驗證,暫且忽略了。
wsgi是 python web框架中的標準,實現了wsgi協議就能寫出一個web框架。python的世界裡不少知名的框架。接下來將會探究一下werkzeug和flask的原始碼以及tornado的原始碼。