項目地址:https://github.com/aosabook/500lines/tree/master/web-server.作者是來自Mozilla的Greg Wilson.項目是用py2寫成.下面文章中貼出的是已經轉換后的能在python3.4下運行的代碼,所以可能會與原先的有少許不同.
簡單地講,你在瀏覽器里輸入一個網址,瀏覽器作為客戶端會通過DNS解析域名找到對應的IP,並將這串字符串的一部分作為請求發給這個IP的服務器,服務器解析字符串,解析出你的請求內容做出相應處理,然后把一串字符串回復給瀏覽器,瀏覽器以一定格式展現這些字符串,就是你看到的網頁.
由於協議是固定的HTTP協議,所以解析這一步基本上可以認為是一樣的,同樣的,將字符串回復給瀏覽器也可以認為是一樣的,所以對不同的server來講,不同的是解析出請求后的處理邏輯.正是基於這一點,python的標准庫里已經給我們提供了很多好用的模塊.
先看一個最簡單的helloworld版的webserver
1 import http.server
2
3 class RequestHandler(http.server.BaseHTTPRequestHandler): 4 '''Handle HTTP requests by returning a fixed 'page'.''' 5 6 # Page to send back. 7 Page = '''\ 8 <html> 9 <body> 10 <p>Hello, web!</p> 11 </body> 12 </html> 13 ''' 14 15 # Handle a GET request. 16 17 def do_GET(self): 18 self.send_response(200) 19 self.send_header("Content-type", "text/html") 20 self.send_header("Content-Length", str(len(self.Page))) 21 self.end_headers() 22 self.wfile.write(bytearray(page,"gbk")) 23 24 #---------------------------------------------------------------------- 25 26 if __name__ == '__main__': 27 serverAddress = ('', 8080) 28 server = http.server.HTTPServer(serverAddress, RequestHandler) 29 server.serve_forever()
代碼非常易懂,main函數里在8080端口啟動http監聽.RequestHandler繼承自http.server.BaseHTTPRequestHandler,這個BaseHTTPRequestHandler已經幫我們做好了解析瀏覽器發來的HTTP請求並調用相應的do_GET()方法的工作.所以我們要做的僅僅是在do_GET()內實現我們的邏輯就好了.
___________________________________________________________________________________________________________________________
現在來看看第二個例子:
1 import http.server
2
3 class RequestHandler(http.server.BaseHTTPRequestHandler): 4 '''Respond to HTTP requests with info about the request.''' 5 6 # Template for page to send back. 7 Page = '''\ 8 <html> 9 <body> 10 <table> 11 <tr> <td>Header</td> <td>Value</td> </tr> 12 <tr> <td>Date and time</td> <td>%(date_time)s</td> </tr> 13 <tr> <td>Client host</td> <td>%(client_host)s</td> </tr> 14 <tr> <td>Client port</td> <td>%(client_port)s</td> </tr> 15 <tr> <td>Command</td> <td>%(command)s</td> </tr> 16 <tr> <td>Path</td> <td>%(path)s</td> </tr> 17 </table> 18 </body> 19 </html> 20 ''' 21 22 # Handle a request by constructing an HTML page that echoes the 23 # request back to the caller. 24 def do_GET(self): 25 page = self.create_page() 26 self.send_page(page) 27 28 # Create an information page to send. 29 def create_page(self): 30 values = { 31 'date_time' : self.date_time_string(), 32 'client_host' : self.client_address[0], 33 'client_port' : self.client_address[1], 34 'command' : self.command, 35 'path' : self.path 36 } 37 page = self.Page % values 38 return page 39 40 # Send the created page. 41 def send_page(self, page): 42 self.send_response(200) 43 self.send_header("Content-type", "text/html") 44 self.send_header("Content-Length", str(len(page))) 45 self.end_headers() 46 self.wfile.write(bytearray(page,"gbk")) 47 48 #---------------------------------------------------------------------- 49 50 if __name__ == '__main__': 51 serverAddress = ('', 8080) 52 server = http.server.HTTPServer(serverAddress, RequestHandler) 53 server.serve_forever()
這個例子也很好懂,例子一里是返回一個靜態的頁面,這一次我們寫一個頁面的模板,然后將解析出來的datatime,clientaddress啥的填充到模板,然后返回這個頁面給瀏覽器.這樣我們就算是能看到一個每次請求內容都會變化的動態的頁面了.
___________________________________________________________________________________________________________________________
現在來看看第三個例子,與例子二相比,這個例子稍微復雜了一些,它添加了一些錯誤處理的代碼,並且不再是簡單地提供一個寫好了模板的頁面:
1 import sys, os, http.server
2
3 class ServerException(Exception): 4 '''For internal error reporting.''' 5 pass 6 7 class RequestHandler(http.server.BaseHTTPRequestHandler): 8 ''' 9 If the requested path maps to a file, that file is served. 10 If anything goes wrong, an error page is constructed. 11 ''' 12 13 # How to display an error. 14 Error_Page = """\ 15 <html> 16 <body> 17 <h1>Error accessing %(path)s</h1> 18 <p>%(msg)s</p> 19 </body> 20 </html> 21 """ 22 23 # Classify and handle request. 24 def do_GET(self): 25 try: 26 27 # Figure out what exactly is being requested. 28 full_path = os.getcwd() + self.path 29 print(self.path," ",full_path) 30 # It doesn't exist... 31 if not os.path.exists(full_path): 32 raise ServerException("'%s' not found" % self.path) 33 34 # ...it's a file... 35 elif os.path.isfile(full_path): 36 self.handle_file(full_path) 37 38 # ...it's something we don't handle. 39 else: 40 raise ServerException("Unknown object '%s'" % self.path) 41 42 # Handle errors. 43 except Exception as msg: 44 self.handle_error(msg) 45 46 def handle_file(self, full_path): 47 try: 48 with open(full_path, 'r') as input: 49 content = input.read() 50 self.send_content(content) 51 except IOError as msg: 52 msg = "'%s' cannot be read: %s" % (self.path, msg) 53 self.handle_error(msg) 54 55 # Handle unknown objects. 56 def handle_error(self, msg): 57 content = self.Error_Page % {'path' : self.path, 58 'msg' : msg} 59 self.send_content(content) 60 61 # Send actual content. 62 def send_content(self, content): 63 self.send_response(200) 64 self.send_header("Content-type", "text/html") 65 self.send_header("Content-Length", str(len(content))) 66 self.end_headers() 67 self.wfile.write(bytearray(content,"gbk")) 68 69 #---------------------------------------------------------------------- 70 71 if __name__ == '__main__': 72 serverAddress = ('', 8080) 73 server = http.server.HTTPServer(serverAddress, RequestHandler) 74 server.serve_forever()
這個例子讀取程序所在的當前目錄下的某個文件,將文件內容返回給瀏覽器.我們看看do_GET(self)這個函數內干了什么,首先是獲取一個全路徑,然后判斷這個全路徑是否存在,存在的話指向的是否為一個文件,是文件則讀取這個文件並返回給瀏覽器,否則則返回一個error_page。
___________________________________________________________________________________________________________________________
現在來看看第四個例子:
1 import sys, os, http.server
2
3 class ServerException(Exception): 4 '''For internal error reporting.''' 5 pass 6 7 class RequestHandler(http.server.BaseHTTPRequestHandler): 8 ''' 9 If the requested path maps to a file, that file is served. 10 If anything goes wrong, an error page is constructed. 11 ''' 12 13 # How to display a directory listing. 14 Listing = '''\ 15 <html> 16 <body> 17 <ul> 18 %s 19 </ul> 20 </body> 21 </html> 22 ''' 23 24 # How to display an error. 25 Error_Page = """\ 26 <html> 27 <body> 28 <h1>Error accessing %(path)s</h1> 29 <p>%(msg)s</p> 30 </body> 31 </html> 32 """ 33 34 # Classify and handle request. 35 def do_GET(self): 36 try: 37 38 # Figure out what exactly is being requested. 39 full_path = os.getcwd() + self.path 40 41 # It doesn't exist... 42 if not os.path.exists(full_path): 43 raise ServerException("'%s' not found" % self.path) 44 45 # ...it's a file... 46 elif os.path.isfile(full_path): 47 self.handle_file(full_path) 48 49 # ...it's a directory... 50 elif os.path.isdir(full_path): 51 self.list_dir(full_path) 52 53 # ...it's something we don't handle. 54 else: 55 raise ServerException("Unknown object '%s'" % self.path) 56 57 # Handle errors. 58 except Exception as msg: 59 self.handle_error(msg) 60 61 def handle_file(self, full_path): 62 try: 63 with open(full_path, 'r') as input: 64 content = input.read() 65 self.send_content(content) 66 except IOError as msg: 67 msg = "'%s' cannot be read: %s" % (self.path, msg) 68 self.handle_error(msg) 69 70 def list_dir(self, full_path): 71 try: 72 entries = os.listdir(full_path) 73 bullets = ['<li>%s</li>' % e for e in entries if not e.startswith('.')] 74 page = self.Listing % '\n'.join(bullets) 75 self.send_content(page) 76 except OSError as msg: 77 msg = "'%s' cannot be listed: %s" % (self.path, msg) 78 self.handle_error(msg) 79 80 # Handle unknown objects. 81 def handle_error(self, msg): 82 content = self.Error_Page % {'path' : self.path, 83 'msg' : msg} 84 self.send_content(content) 85 86 # Send actual content. 87 def send_content(self, content): 88 self.send_response(200) 89 self.send_header("Content-type", "text/html") 90 self.send_header("Content-Length", str(len(content))) 91 self.end_headers() 92 self.wfile.write(bytearray(content,"gbk")) 93 94 #---------------------------------------------------------------------- 95 96 if __name__ == '__main__': 97 serverAddress = ('', 8080) 98 server = http.server.HTTPServer(serverAddress, RequestHandler) 99 server.serve_forever()
這個例子和上個例子是類似的,不過是多了個對全路徑指向的是一個目錄而不是一個文件這種狀況的處理.
——————————————————————————————————————————————————————————————————————————————————
第五個例子比上述的看起來又要復雜一些,不過其實只是多了一個命令行的處理以及一些錯誤處理函數而已.getopt模塊用法看這里:https://docs.python.org/3/library/getopt.html?highlight=getopt#module-getopt,
在例子4和例子3中,我們拼接全路徑是通過full_path = os.getcwd() + self.path來拼接.也就是說比如你的server.py位於D盤,那么你的full_path就只能是D:\xxxxx這種形式.
那么在例子5里,我們通過在啟動server.py時通過命令行的方式(比如:python server.py -v C:\)傳參一個根目錄,那么這時候就能處理C:\XXXX路徑的文件了.
1 import sys, os, http.server
2
3 class RequestHandler(http.server.BaseHTTPRequestHandler): 4 5 # Root of files being served. 6 Root_Directory = None 7 8 # Is debugging output on? 9 Debug = False 10 11 # HTTP error codes. 12 ERR_NO_PERM = 403 13 ERR_NOT_FOUND = 404 14 ERR_INTERNAL = 500 15 16 # How to display a single item in a directory listing. 17 Listing_Item = "<li>%s</li>" 18 19 # How to display a whole page of listings. 20 Listing_Page = """\ 21 <html> 22 <body> 23 <h1>Listing for %(path)s</h1> 24 <ul> 25 %(filler)s 26 </ul> 27 </body> 28 </html> 29 """ 30 31 # Classify and handle request. 32 def do_GET(self): 33 34 self.log("path is '%s'" % self.path) 35 36 # Handle any errors that arise. 37 try: 38 39 # Class not yet initialized. 40 if self.Root_Directory is None: 41 self.err_internal("Root directory not set") 42 return 43 44 # Figure out what exactly is being requested. 45 abs_path = self.create_abs_path() 46 self.log("abs_path is '%s'" % abs_path) 47 48 # It isn't below the root path. 49 if not self.is_parent_dir(self.Root_Directory, abs_path): 50 self.log("abs_path not below root directory") 51 msg = "Path '%s' not below root directory '%s'" % \ 52 (abs_path, self.Root_Directory) 53 self.err_no_perm(msg) 54 55 # It doesn't exist. 56 elif not os.path.exists(abs_path): 57 self.log("abs_path doesn't exist") 58 self.err_not_found(abs_path) 59 60 # It's a file. 61 elif os.path.isfile(abs_path): 62 self.log("abs_path is a file") 63 self.handle_file(abs_path) 64 65 # It's a directory. 66 elif os.path.isdir(abs_path): 67 self.log("abs_path is a directory") 68 self.handle_dir(abs_path) 69 70 # ...we can't tell. 71 else: 72 self.log("can't tell what abs_path is") 73 self.err_not_found(abs_path) 74 75 # Handle general errors. 76 except Exception as msg: 77 self.err_internal("Unexpected exception: %s" % msg) 78 79 def create_abs_path(self): 80 head = os.path.abspath(self.Root_Directory) 81 result = os.path.normpath(head + self.path) 82 return result 83 84 def is_parent_dir(self, left, right): 85 return os.path.commonprefix([left, right]) == left 86 87 def handle_file(self, abs_path): 88 try: 89 input = open(abs_path, "r") 90 content = input.read() 91 input.close() 92 self.send_content(content) 93 except IOError as msg: 94 msg = "'%s' cannot be read: %s" % (self.path, msg) 95 self.err_no_perm(msg) 96 97 def handle_dir(self, abs_path): 98 try: 99 listing = os.listdir(abs_path) 100 filler = '\n'.join([(self.Listing_Item % item) for item in listing]) 101 content = self.Listing_Page % {'path' : self.path, 102 'filler' : filler} 103 self.send_content(content) 104 except IOError as msg: 105 msg = "'%s' cannot be listed: %s" % (self.path, msg) 106 self.send_error(ERR_NO_PERM, msg) 107 108 # Send actual content. 109 def send_content(self, content): 110 self.send_response(200) 111 self.send_header("Content-type", "text/html") 112 self.send_header("Content-Length", str(len(content))) 113 self.end_headers() 114 self.wfile.write(bytearray(content,"gbk")) 115 116 # Report internal errors. 117 def err_internal(self, msg): 118 self.send_error(self.ERR_INTERNAL, msg) 119 120 # Handle missing object errors. 121 def err_not_found(self, abs_path): 122 self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path) 123 124 # Handle no permission errors. 125 def err_no_perm(self, msg): 126 self.send_error(self.ERR_NO_PERM, msg) 127 128 # Write a log message if in debugging mode 129 def log(self, msg): 130 if self.Debug: 131 print(msg) 132 133 #---------------------------------------------------------------------- 134 135 if __name__ == '__main__': 136 137 # Main libraries 138 import getopt 139 140 # How to use 141 Usage = "server.py [-v] root_directory" 142 143 # Handle command-line arguments 144 options, rest = getopt.getopt(sys.argv[1:], "v") 145 #print(sys.argv[1:]) 146 #print(options,rest) 147 for (flag, arg) in options: 148 if flag == "-v": 149 RequestHandler.Debug = True 150 else: 151 print(Usage, file=sys.stderr) 152 sys.exit(1) 153 154 if not rest: 155 print(Usage, file=sys.stderr) 156 sys.exit(1) 157 root = os.path.abspath(rest[0]) 158 if not os.path.isdir(root): 159 print("No such directory '%s'" % root, file=sys.stderr) 160 sys.exit(1) 161 RequestHandler.Root_Directory = root 162 163 # Create and run server. 164 server_address = ('', 8080) 165 server = http.server.HTTPServer(server_address, RequestHandler) 166 server.serve_forever()
--------------------------------------------------------------------------------------------------------------------------------------------------------------------在前面的例子中我們注意到在http_header里類型我們總是填寫的“text/html”.如果瀏覽器請求的是一個圖片呢?你可以運行例子5,你會發現瀏覽器是無法正常顯示這個圖片的.
self.send_header("Content-type", "text/html")
例子6與例子5的區別只是加了一個文件類型(例如是個文本啊還是個圖片啊)的判斷,從而在回發content的時候告知瀏覽器content-type以使其知道應該如何處理這種文件.有關mime type的知識可以看這里
http://www.cnblogs.com/jsean/articles/1610265.html
1 import sys, os, http.server, mimetypes
2 class RequestHandler(http.server.BaseHTTPRequestHandler): 3 4 # Root of files being served. 5 Root_Directory = None 6 7 # Is debugging output on? 8 Debug = False 9 10 # HTTP error codes. 11 ERR_NO_PERM = 403 12 ERR_NOT_FOUND = 404 13 ERR_INTERNAL = 500 14 15 # How to display a single item in a directory listing. 16 Listing_Item = "<li>%s</li>" 17 18 # How to display a whole page of listings. 19 Listing_Page = """\ 20 <html> 21 <body> 22 <h1>Listing for %(path)s</h1> 23 <ul> 24 %(filler)s 25 </ul> 26 </body> 27 </html> 28 """ 29 30 # MIME types of files. 31 File_Types = mimetypes.types_map 32 33 # Classify and handle request. 34 def do_GET(self): 35 36 self.log("path is '%s'" % self.path) 37 38 # Handle any errors that arise. 39 try: 40 41 # Class not yet initialized. 42 if self.Root_Directory is None: 43 self.err_internal("Root directory not set") 44 return 45 46 # Figure out what exactly is being requested. 47 abs_path = self.create_abs_path() 48 self.log("abs_path is '%s'" % abs_path) 49 50 # It isn't below the root path. 51 if not self.is_parent_dir(self.Root_Directory, abs_path): 52 self.log("abs_path not below root directory") 53 msg = "Path '%s' not below root directory '%s'" % \ 54 (abs_path, self.Root_Directory) 55 self.err_no_perm(msg) 56 57 # It doesn't exist. 58 elif not os.path.exists(abs_path): 59 self.log("abs_path doesn't exist") 60 self.err_not_found(abs_path) 61 62 # It's a file. 63 elif os.path.isfile(abs_path): 64 self.log("abs_path is a file") 65 self.handle_file(abs_path) 66 67 # It's a directory. 68 elif os.path.isdir(abs_path): 69 self.log("abs_path is a directory") 70 self.handle_dir(abs_path) 71 72 # ...we can't tell. 73 else: 74 self.log("can't tell what abs_path is") 75 self.err_not_found(abs_path) 76 77 # Handle general errors. 78 except Exception as msg: 79 self.err_internal("Unexpected exception: %s" % msg) 80 81 def create_abs_path(self): 82 head = os.path.abspath(self.Root_Directory) 83 result = os.path.normpath(head + self.path) 84 return result 85 86 def is_parent_dir(self, left, right): 87 return os.path.commonprefix([left, right]) == left 88 89 # Guess the MIME type of a file from its name. 90 def guess_file_type(self, path): 91 base, ext = os.path.splitext(path) 92 if ext in self.File_Types: 93 return self.File_Types[ext] 94 ext = ext.lower() 95 if ext in self.File_Types: 96 return self.File_Types[ext] 97 return self.File_Types[''] 98 99 # Handle files. Must read in binary mode! 100 def handle_file(self, abs_path): 101 try: 102 input = open(abs_path, "rb") 103 content = input.read() 104 input.close() 105 fileType = self.guess_file_type(abs_path) 106 print(fileType) 107 self.send_content(content, fileType) 108 except IOError as msg: 109 msg = "'%s' cannot be read: %s" % (self.path, msg) 110 self.err_no_perm(msg) 111 112 # Handle directories. 113 def handle_dir(self, abs_path): 114 try: 115 listing = os.listdir(abs_path) 116 filler = '\n'.join([(self.Listing_Item % item) for item in listing]) 117 content = self.Listing_Page % {'path' : self.path, 118 'filler' : filler} 119 print(type(content)) 120 self.send_content(content.encode()) 121 except IOError as msg: 122 msg = "'%s' cannot be listed: %s" % (self.path, msg) 123 self.err_no_perm(msg) 124 125 # Send actual content. 126 def send_content(self, content, fileType="text/html"): 127 length = str(len(content)) 128 self.log("sending content, fileType '%s', length %s" % (fileType, length)) 129 self.send_response(200) 130 self.send_header("Content-type", fileType) 131 self.send_header("Content-Length", length) 132 self.end_headers() 133 print(type(self.wfile),type(content)) 134 self.wfile.write(content) 135 136 # Report internal errors. 137 def err_internal(self, msg): 138 self.send_error(self.ERR_INTERNAL, msg) 139 140 # Handle missing object errors. 141 def err_not_found(self, abs_path): 142 self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path) 143 144 # Handle no permission errors. 145 def err_no_perm(self, msg): 146 self.send_error(self.ERR_NO_PERM, msg) 147 148 # Write a log message if in debugging mode 149 def log(self, msg): 150 if self.Debug: 151 print(msg) 152 153 #---------------------------------------------------------------------- 154 155 if __name__ == '__main__': 156 157 # Main libraries 158 import getopt 159 160 # How to use 161 Usage = "server.py [-v] root_directory" 162 163 # Handle command-line arguments 164 options, rest = getopt.getopt(sys.argv[1:], "v") 165 166 for (flag, arg) in options: 167 if flag == "-v": 168 RequestHandler.Debug = True 169 else: 170 print(Usage, file=sys.stderr) 171 sys.exit(1) 172 173 if not rest: 174 print(Usage, file=sys.stderr) 175 sys.exit(1) 176 root = os.path.abspath(rest[0]) 177 if not os.path.isdir(root): 178 print("No such directory '%s'" % root, file=sys.stderr) 179 sys.exit(1) 180 RequestHandler.Root_Directory = root 181 182 # Create and run server. 183 server_address = ('', 8080) 184 server = http.server.HTTPServer(server_address, RequestHandler) 185 server.serve_forever()
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
以上的例子中,我們返回給客戶端的內容,不管是txt也好,html也好,png也好,都可以看做是靜態的文件,是不能被執行的.以下這個例子通過subprocess模塊的Popen()可以執行一個.py文件,然后將執行的py文件產生的stdout內容返回給瀏覽器.有關subprocess的內容可以看這里:https://docs.python.org/3.4/library/subprocess.html
1 import sys, os, http.server, mimetypes, gettext,subprocess
2
3 class RequestHandler(http.server.BaseHTTPRequestHandler): 4 5 # Root of files being served. 6 Root_Directory = None 7 8 # Is debugging output on? 9 Debug = False 10 11 # HTTP error codes. 12 ERR_NO_PERM = 403 13 ERR_NOT_FOUND = 404 14 ERR_INTERNAL = 500 15 16 # MIME types of files. 17 File_Types = mimetypes.types_map 18 19 # Filename extensions that identify executables. 20 Exec_Extensions = { 21 ".py" : None 22 } 23 24 # Classify and handle request. 25 def do_GET(self): 26 27 # Handle any errors that arise. 28 try: 29 30 # Class not yet initialized. 31 if self.Root_Directory is None: 32 self.err_internal("Root directory not set") 33 return 34 35 # Figure out what exactly is being requested. 36 abs_path, query_params = self.parse_path() 37 self.log("abs_path is '%s'" % abs_path) 38 self.log("query_params is '%s'" % query_params) 39 40 # It isn't below the root path. 41 if not self.is_parent_dir(self.Root_Directory, abs_path): 42 self.log("abs_path not below root directory") 43 self.err_no_perm("Path '%s' not below root directory '%s'" % \ 44 (abs_path, self.Root_Directory)) 45 46 # It doesn't exist. 47 elif not os.path.exists(abs_path): 48 self.log("abs_path doesn't exist") 49 self.err_not_found(abs_path) 50 51 # It's a file. (Ignore query parameters if the file is 52 # not being executed.) 53 elif os.path.isfile(abs_path): 54 if self.is_executable(abs_path): 55 self.log("abs_path is an executable") 56 self.handle_executable(abs_path, query_params) 57 else: 58 self.log("abs_path is a file") 59 self.handle_static_file(abs_path) 60 61 # It's a directory --- ignore query parameters. 62 elif os.path.isdir(abs_path): 63 self.log("abs_path is a directory") 64 self.handle_dir(abs_path) 65 66 # ...we can't tell. 67 else: 68 self.log("can't tell what abs_path is") 69 self.err_not_found(abs_path) 70 71 # Handle general errors. 72 except Exception as msg: 73 self.err_internal("Unexpected exception in main despatch: %s" % msg) 74 75 def parse_path(self): 76 '''Create the absolute path for a request, and extract the query 77 parameter string (if any).''' 78 parts = self.path.split("?") 79 if len(parts) == 1: 80 request_path, queryString = self.path, "" 81 elif len(parts) == 2: 82 request_path, queryString = parts 83 else: 84 pass 85 head = os.path.abspath(self.Root_Directory) 86 result = os.path.normpath(head + request_path) 87 return result, queryString 88 89 def is_parent_dir(self, left, right): 90 return os.path.commonprefix([left, right]) == left 91 92 def guess_file_type(self, path): 93 base, ext = os.path.splitext(path) 94 if ext in self.File_Types: 95 return self.File_Types[ext] 96 ext = ext.lower() 97 if ext in self.File_Types: 98 return self.File_Types[ext] 99 return self.File_Types[''] 100 101 def is_executable(self, abs_path): 102 '''Does this path map to an executable file?''' 103 root, ext = os.path.splitext(abs_path) 104 return ext in self.Exec_Extensions 105 106 def handle_static_file(self, abs_path): 107 '''Handle static files. Must read in binary mode!''' 108 try: 109 input = file(abs_path, "rb") 110 content = input.read() 111 input.close() 112 file_type = self.guess_file_type(abs_path) 113 self.send_content(content, file_type) 114 except IOError as msg: 115 self.err_no_perm("'%s' cannot be read: %s" % (self.path, msg)) 116 117 # Handle directories. 118 def handle_dir(self, abs_path): 119 120 # How to display a single item in a directory listing. 121 listing_item = "<li>%s</li>" 122 123 # How to display a whole page of listings. 124 listing_page = \ 125 "<html>" + \ 126 "<body>" + \ 127 "<h1>Listing for " + "%(path)s" + "</h1>" + \ 128 "<ul>" + \ 129 "%(filler)s" + \ 130 "</ul>" + \ 131 "</body>" + \ 132 "</html>" 133 134 try: 135 listing = os.listdir(abs_path) 136 filler = '\n'.join([(listing_item % item) for item in listing]) 137 content = listing_page % {'path' : self.path, 138 'filler' : filler} 139 self.send_content(content) 140 except IOError as msg: 141 self.err_no_perm("'%s' cannot be listed: %s" % msg) 142 143 # Handle executable file. 144 def handle_executable(self, abs_path, query_params): 145 # Passing query parameters? 146 if query_params: 147 os.environ["REQUEST_METHOD"] = "GET" 148 os.environ["QUERY_STRING"] = query_params 149 cmd = "python " + abs_path 150 #p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,stdin=PIPE, stdout=PIPE, close_fds=True) 151 p = subprocess.Popen(cmd,stdin=subprocess.PIPE, stdout=subprocess.PIPE) 152 print(type(p),p) 153 (childInput, childOutput) = (p.stdin,p.stdout) 154 print(cmd,childInput,childOutput) 155 #childInput, childOutput = subprocess.popen2(cmd) 156 childInput.close() 157 response = childOutput.read() 158 childOutput.close() 159 self.log("handle_executable: response length is %d" % len(response)) 160 self.send_response(200) 161 self.wfile.write(response) 162 163 # Send actual content. 164 def send_content(self, content, fileType="text/html"): 165 length = str(len(content)) 166 self.log("sending content, fileType '%s', length %s" % (fileType, length)) 167 self.send_response(200) 168 self.send_header("Content-type", fileType) 169 self.send_header("Content-Length", length) 170 self.end_headers() 171 self.wfile.write(content) 172 173 # Report internal errors. 174 def err_internal(self, msg): 175 self.send_error(self.ERR_INTERNAL, msg) 176 177 # Handle missing object errors. 178 def err_not_found(self, abs_path): 179 self.send_error(self.ERR_NOT_FOUND, "'%s' not found" % self.path) 180 181 # Handle no permission errors. 182 def err_no_perm(self, msg): 183 self.send_error(self.ERR_NO_PERM, msg) 184 185 # Handle execution errors. 186 def errExec(self, msg): 187 self.send_error(self.ERR_NO_PERM, msg) 188 189 # Write a log message if in debugging mode 190 def log(self, msg): 191 if self.Debug: 192 print("nitinat:", msg) 193 194 #---------------------------------------------------------------------- 195 196 if __name__ == '__main__': 197 198 # Main libraries 199 import getopt 200 201 # How to handle fatal startup errors 202 def fatal(msg): 203 print("nitinat:", msg, file=sys.stderr) 204 sys.exit(1) 205 206 # Defaults 207 host = '' 208 port = 8080 209 root = None 210 211 # How to use 212 Usage = "server.py [-h host] [-p port] [-v] -r|Root_Directory" 213 214 # Handle command-line arguments 215 options, rest = getopt.getopt(sys.argv[1:], "h:p:rv") 216 217 for (flag, arg) in options: 218 if flag == "-h": 219 host = arg 220 if not arg: 221 msg = "No host given with -h" 222 fatal(msg) 223 elif flag == "-p": 224 try: 225 port = int(arg) 226 except ValueError as msg: 227 fatal("Unable to convert '%s' to integer: %s" % (arg, msg)) 228 elif flag == "-r": 229 root = os.getcwd() 230 elif flag == "-v": 231 RequestHandler.Debug = True 232 else: 233 fatal(Usage) 234 235 # Make sure root directory is set, and is a directory. 236 if (root and rest) or (not root and not rest): 237 fatal(Usage) 238 if not root: 239 root = os.path.abspath(rest[0]) 240 if not os.path.isdir(root): 241 fatal("No such directory '%s'" % root) 242 RequestHandler.Root_Directory = root 243 244 # Create and run server. 245 server_address = (host, port) 246 server = http.server.HTTPServer(server_address, RequestHandler) 247 server.serve_forever()
以上就是這個500行的簡易小項目的全部內容了,可以看出來,雖然每一個程序都越來越復雜,其實程序的骨架並沒有變,我們不斷豐富的只是解析出請求后具體的處理邏輯代碼而已,由於python的標准庫已經幫我們做了許多諸如解析請求啊,回發內容啊等等這些內容,我們用python寫起web來還是蠻容易的.雖然只是很簡單的一些小程序,但是已經描述出了基本的web的寫法,還是值得一看的.從git上下載該項目以后,里面的chapter.md可以好好看一看,里面簡要介紹了web和HTTP協議的一些知識.你可以用markdownpad或者直接在線https://www.zybuluo.com/mdeditor來閱讀這個.md文件.