项目地址: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文件.

[500lines]500行代码写web server的更多相关文章

  1. python实战:用70行代码写了一个山炮计算器!

    python实战训练:用70行代码写了个山炮计算器! 好了...好了...各位因为我是三年级而发牢骚的各位伙伴们,我第一次为大家插播了python的基础实战训练.这个,我是想给,那些python基础一 ...

  2. 一步一步手写GIS开源项目-(1)500行代码实现基础GIS展示功能

    1.开篇 大学毕业工作已经两年了,上学那会就很想研读一份开源GIS的源码,苦于自己知识和理解有限,而市面上也没有什么由浅入深讲解开源gis原理的书籍,大多都是开源项目简介以及项目的简单应用.对于初级程 ...

  3. [置顶] Embedded Server:像写main函数一样写Web Server

    1.传统的JEE Web Server 传统的JEE中,如果我们想要部署一个Web Application,我们需要首先安装一个Container Server,如JBoss,WebLogic,Tom ...

  4. 浏览器远程编写python代码--jupyter web server

    公司分配了开发机,偶尔需要写一些python自动化脚本.为了提高编写效率,可以开发机上起一个jupyter web server,然后在电脑chrome浏览器进行编辑. 以下步骤均在开发机上操作. 安 ...

  5. 500行代码了解Mecached缓存客户端驱动原理

    原创不易,求分享.求一键三连 缓存一般是用来加速数据访问的效率,在获取数据耗时高的场景下使用缓存可以有效的提高数据获取的效率. 比如,先从memcached中获取数据,如果没有则查询mysql中的数据 ...

  6. 【编程教室】PONG - 100行代码写一个弹球游戏

    大家好,欢迎来到 Crossin的编程教室 ! 今天跟大家讲一讲:如何做游戏 游戏的主题是弹球游戏<PONG>,它是史上第一款街机游戏.因此选它作为我这个游戏开发系列的第一期主题. 游戏引 ...

  7. 50行代码写的一个插件,破解一个H5小游戏

    小游戏链接:测测你的眼睛对色差的辨识度http://www.webhek.com/post/color-test.html?from=timeline 废话不多说,先放代码: window.onloa ...

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

    这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手. 帮助蹲厕族.YP族.饭圈女孩在无聊之余可以有一样东西让他们振作起来!让他们的左手 / 右 ...

  9. 非常好的开源C项目tinyhttpd(500行代码)

    编译命令 gcc -W -Wall -lpthread -o httpd httpd.c 源码 #include <stdio.h> #include <sys/socket.h&g ...

随机推荐

  1. POJ1416 Shredding Company(dfs)

    题目链接. 分析: 这题从早上调到现在.也不算太麻烦,细节吧. 每个数字都只有两种状态,加入前一序列和不加入前一序列.DFS枚举. #include <iostream> #include ...

  2. Visual Studio Code尝试体验

    背景了解 偶然间看到一篇大赞Visual Studio Code的文章,就搜索了一下,发现网上基本一致的好评.虽然微软在2015年4月29号 Build 2015 大会上才发布,但免费,轻量,跨平台版 ...

  3. UDP 收/发 广播包

    网络通信基础 如果网络中两个主机上的应用程序要相互通信,其一要知道彼此的IP,其二要知道程序可监听的端口.因为同一主机上的程序使用网络是通过端口号来区分的. UDP Socket的使用过程: 1. 初 ...

  4. 【贪心】Vijos P1615 旅行

    题目链接: https://vijos.org/p/1615 题目大意: N条路,路的高度给你,走一条路的耗费体力是从上一条路的高度到当前路高度的绝对值差. 可以改变一条路的高度,耗费的体力等于改变前 ...

  5. Dolls - 4160(简单二分图匹配)

    题意:有一些箱子,大箱子可以套小箱子,但是必须h>h,w>w,l>l,求出来最外面能剩下几个箱子无法被嵌套.   分析:思考每个箱子都只会被别的箱子套一次,所以构成一二分匹配模型,只 ...

  6. springboot 配置文件 .properties和.yml的写法区别

    例如 :    redis配置的properties或yml文件,如下: spring.redis.cluster.nodes[]= spring.redis.cluster.nodes[]= 或 s ...

  7. Core OS 层

    Core OS层的底层功能是很多其他技术的构建基础.通常情况下,这些功能不会直接应用于应用程序,而是应用于其他框架.但是,在直接处理安全事务或和某个外设通讯的时候,则必须要应用到该层的框架. Acce ...

  8. 文字保护纱-Material Design

    Ok,关于这个Material Design 都快被说烂了,他被开发者越来越熟悉的程度,却与市场上的单薄身影形成了鲜明的对比,以至于每当我提及Material Design时就像祥林嫂附身一样. 有些 ...

  9. 在Quick-cocos2dx中使用云风pbc解析Protocol Buffers,支持win、mac、ios、android

    本例主要介绍 如何将 pbc 集成到quick-cocos2dx框架中,让我们的cocos2dx客户端Lua拥有编解码Protocol Buffers能力. 参考: 云风pbc的用法: http:// ...

  10. Linux 开机自检的设置(tune2fs和fsck)

      tune2fs和fsck的用法 tune2fs--调整ext2/ext3文件系统特性的工具. -l <device> 查看文件系统信息 -c <count> 设置强制自检的 ...