python 從SocketServer到 WSGIServer 源碼分析、

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的數據。。

相關文章
相關標籤/搜索