[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 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文件.

时间: 2024-10-07 05:50:27

[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的服务器,服务器解析字符串,解析出你的请求内容做出相应处理,然后把一串字

适合练习的10个Python项目,每个项目都不到500行代码

以下10个练手项目均摘录自一本尚未出版的 Python 神书<500 Lines or Less>,尽管没有出版,但其 review 版已在官方博客放出. 实现一个网络爬虫 不多说,几百行代码实现高效的网络爬虫, 高效! 项目链接:http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html Python 实现数据库 如何用 Python 实现一个数据库,支持 query,index, transaction, 两

js280行代码写2048

2048 原作者就是用Js写的,一直想尝试,但久久未动手. 昨天教学生学习JS代码.不妨就做个有趣的游戏好了.2048这么火,是一个不错的选择. 思路: 1. 数组 ,2维数组4x4 2. 移动算法,移动后有数字的对齐,无数字(我用的0,但不显示)补齐. 移动前 移动后(注意程序合并了第一行2个2,并产生了新的2) 移动算法分2步: 第一步骤:移动 第二步骤:合并 移动代码参考: function left(t,i) { var j; var len = t[i].length; for (j=

都说Djnago框架重,那就让哥用15行代码写个django web程序!

很多初学django的小伙伴都会了解到,django是个大而全的网络框架,本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能.要学这么多内容,要学到猴年马月啊!? 不过世界真是奇妙,现在咱们就在猴年马月里!2016年是猴年,按农历计算,6月5日至7月3日是庚午月,正好是"猴年"里的"马月".那么问题来了:你想不想在猴年马月里学会django呢? 下面我们尝试一下,用15行代码结合django写个web程序,来一次django的清爽体验. djang

反-反爬虫:用几行代码写出和人类一样的动态爬虫

欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:李大伟 Phantomjs简介 什么是Phantomjs Phantomjs官网介绍是:不需要浏览器的完整web协议栈(Full web stack No browser required),也就是常说的无头浏览器--或者好听点叫做:无界面的web解析器. Phantomjs的特点 由于"无头"--免去了渲染可视化的网页界面,她的速度要比一般的浏览器快不少,又因为她是完整的web协议栈,所以不仅仅提供了JavaScri

1000行代码写小游戏(终)

最后献上完整的1000行代码,基本功能已经完成,可以通过配置小怪和矿的位置和大小控制玩家时长和难度: -------------------------------------------------------------------------------------------- -- Added by ??? ------------------------------------------------------------------------------------------

一起来写web server 06 -- 单线程非阻塞IO版本

阻塞IO的效率是在是低下,你如果要写高性能的web server的话,你必须使用非阻塞IO. 非阻塞IO的读写 在谈到非阻塞IO之前,必须先谈一谈阻塞IO,在网络编程中,我们假设有一个监听套接字的sockfd,这个sockfd是你调用了socket,listen, bind这一大票的函数得到的一个文件描述符.其实它默认就是阻塞的,具体的表现是: 使用accept函数监听sockfd时,如果没有连接到来,这个函数会一直阻塞在那里. 对sockfd调用recv函数的时候,如果对方还没有发送数据过来,

教你10行代码写侧滑菜单

原帖发表于传智播客黑马训程序员论坛,地址:http://bbs.itheima.com/thread-167442-1-1.html 先来看个侧滑菜单效果: 上面分别为:初始状态->滑动中->松开打开菜单 你造吗?看完本文章,写这种侧滑菜单,so easy for you! 你造吗?其实写这个侧滑菜单,只需要10行手写代码就能搞定! 你造吗?看完本文章,当你再听到产品说"我要这个效果"时,再也不会底气不足! 在Android开发中,自定义View效果是属于高级的部分.因为常

500行代码,教你用python写个微信飞机大战

这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手. 帮助蹲厕族.YP族.饭圈女孩在无聊之余可以有一样东西让他们振作起来!让他们的左手 / 右手有节奏有韵律的朝着同一个方向来回移动起来! 这是史诗级的发明,是浓墨重彩的一笔,是-- 在一阵抽搐后,我结束了游戏,瞬时觉得一切都索然无味,正在我进入贤者模式时,突然想到,如果我可以让更多人已不同的方式体会到这种美轮美奂的感觉岂不美哉? 所以我打开电脑,创建了一个 plan_game.py-- 先