python SimpleHTTPServer源碼學習


SimpleHTTPServer.SimpleHTTPRequestHandler繼承了BaseHTTPServer.BaseHTTPRequestHandler。

源碼中主要實現了BaseHTTPServer.BaseHTTPRequestHandler處理時需要調用的do_Head()和do_GET()函數。這類函數主要是在BaseHTTPRequestHandler在接受請求並判斷請求頭中的command之后調用的。

 def handle_one_request(self):
            ... ...
            mname = 'do_' + self.command
            if not hasattr(self, mname):
                self.send_error(501, "Unsupported method (%r)" % self.command)
                return
            method = getattr(self, mname)
            method()
            ... ...

 因此,在我們使用SimpleHTTPServer 對web請求處理時基本都需要調用這個method(),當然,其他異常情況除外。

SimpleHTTPServer.SimpleHTTPRequestHandler默認的處理是,如果在執行該腳本的當前目錄含有 index.html或index.htm時,將把這個文件的html內容作為首頁,如果不存在,則在界面顯示當前目錄下的文件夾內容,並內部將其設置html頁面展現方式。

    def list_directory(self, path):
        """Helper to produce a directory listing (absent index.html).

        Return value is either a file object, or None (indicating an
        error).  In either case, the headers are sent, making the
        interface the same as for send_head().

        """
        try:
            list = os.listdir(path)
        except os.error:
            self.send_error(404, "No permission to list directory")
            return None
        list.sort(key=lambda a: a.lower())
        f = StringIO()
        displaypath = cgi.escape(urllib.unquote(self.path))
        f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
        f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
        f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
        f.write("<hr>\n<ul>\n")
        for name in list:
            fullname = os.path.join(path, name)
            displayname = linkname = name
            # Append / for directories or @ for symbolic links
            if os.path.isdir(fullname):
                displayname = name + "/"
                linkname = name + "/"
            if os.path.islink(fullname):
                displayname = name + "@"
                # Note: a link to a directory displays with @ and links with /
            f.write('<li><a href="%s">%s</a>\n'
                    % (urllib.quote(linkname), cgi.escape(displayname)))
        f.write("</ul>\n<hr>\n</body>\n</html>\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        encoding = sys.getfilesystemencoding()
        self.send_header("Content-type", "text/html; charset=%s" % encoding)
        self.send_header("Content-Length", str(length))
        self.end_headers()
        return f
SimpleHTTPRequestHandler中list_directory()

其實對於socket的請求整理是在SocketServer.TCPServer中處理的,對web請求頭的處理是在BaseHTTPServer.BaseHTTPRequestHandler中處理的,其對頭的類型,版本等作了處理。而對於請求的回應則在子類SimpleHTTPServer.SimpleHTTPRequestHandler中處理。

那么,SimpleHTTPServer.SimpleHTTPRequestHandler是如何作出上述說明的請求的呢?

首先,Simple通過send_head()函數內部預先分析了請求的url路徑,然后提取路徑與當前目錄路徑組合得到請求的絕對路徑地址,如果在該路徑下存在index.html或index.htm文件則將這個文件內容打開並設置回饋頭的內容,寫入文件內容的長度和內容的類型,如果沒有這個文件,則將獲取當前目錄下的內容,創建一個文件緩存寫入一個html格式的內容,其中寫明當前目錄所具有的內容並設置超鏈接,使得用戶點擊時服務器能正確的回饋對應內容。

我們發現send_head()其實發送的請求頭是根據請求內容進行設置的,也就是說在send_head()中,Simple已經把請求data准備好了,所以在send_head()之后只需要調用self.copyfile(f, self.wfile)將文件對象或緩存文件對象中的內容寫入請求流對象中即可。

至於其他函數,都是為這些作准備的。

*值得注意的是,在讀取本地文件回饋給客戶端時要注意文件需要以rb的方式,即二進制方式去讀,這樣就避免文本流中換行了,也能正確的就算出流的長度(長度是作為回饋頭的一部分反饋出去的)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM