首页 > 代码库 > [500lines]500行代码学写web server
[500lines]500行代码学写web server
项目地址: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 the23 # 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.path36 }37 page = self.Page % values38 return page39 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.path29 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 %s19 </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.path40 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 mode129 def log(self, msg):130 if self.Debug:131 print(msg)132 133 #----------------------------------------------------------------------134 135 if __name__ == ‘__main__‘:136 137 # Main libraries138 import getopt139 140 # How to use141 Usage = "server.py [-v] root_directory"142 143 # Handle command-line arguments144 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 = True150 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 = root162 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 mode149 def log(self, msg):150 if self.Debug:151 print(msg)152 153 #----------------------------------------------------------------------154 155 if __name__ == ‘__main__‘:156 157 # Main libraries158 import getopt159 160 # How to use161 Usage = "server.py [-v] root_directory"162 163 # Handle command-line arguments164 options, rest = getopt.getopt(sys.argv[1:], "v")165 166 for (flag, arg) in options:167 if flag == "-v":168 RequestHandler.Debug = True169 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 = root181 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_Extensions105 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_params149 cmd = "python " + abs_path150 #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 mode190 def log(self, msg):191 if self.Debug:192 print("nitinat:", msg)193 194 #----------------------------------------------------------------------195 196 if __name__ == ‘__main__‘:197 198 # Main libraries199 import getopt200 201 # How to handle fatal startup errors202 def fatal(msg):203 print("nitinat:", msg, file=sys.stderr)204 sys.exit(1)205 206 # Defaults207 host = ‘‘208 port = 8080209 root = None210 211 # How to use212 Usage = "server.py [-h host] [-p port] [-v] -r|Root_Directory"213 214 # Handle command-line arguments215 options, rest = getopt.getopt(sys.argv[1:], "h:p:rv")216 217 for (flag, arg) in options:218 if flag == "-h":219 host = arg220 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 = True232 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 = root243 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文件.
[500lines]500行代码学写web server