基于python创建一个简单的HTTP-WEB服务器

时间:2024-01-31 13:52:37
  • 背景

大多数情况下主机资源只有开发和测试相关人员可以登录直接操作,且有些特定情况“答辩、演示、远程”等这些场景下是无法直接登录主机的。web是所有终端用户都可以访问了,解决了人员权限与特定场景带来的问题。那么我们就来看看最简单的web服务器是怎么创建的~~

  • 具体实现

首先搭建python环境,涉及问题请移步http://www.cnblogs.com/xnchll/p/6431664.html。python内建模块SimpleHTTPServer,源码如下路径是/usr/lib64/python2.6/SimpleHTTPServer.py,有兴趣可看看

  1 """Simple HTTP Server.
  2 
  3 This module builds on BaseHTTPServer by implementing the standard GET
  4 and HEAD requests in a fairly straightforward manner.
  5 
  6 """
  7 
  8 
  9 __version__ = "0.6"
 10 
 11 __all__ = ["SimpleHTTPRequestHandler"]
 12 
 13 import os
 14 import posixpath
 15 import BaseHTTPServer
 16 import urllib
 17 import cgi
 18 import sys
 19 import shutil
 20 import mimetypes
 21 try:
 22     from cStringIO import StringIO
 23 except ImportError:
 24     from StringIO import StringIO
 25 
 26 
 27 class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 28 
 29     """Simple HTTP request handler with GET and HEAD commands.
 30 
 31     This serves files from the current directory and any of its
 32     subdirectories.  The MIME type for files is determined by
 33     calling the .guess_type() method.
 34 
 35     The GET and HEAD requests are identical except that the HEAD
 36     request omits the actual contents of the file.
 37 
 38     """
 39 
 40     server_version = "SimpleHTTP/" + __version__
 41 
 42     def do_GET(self):
 43         """Serve a GET request."""
 44         f = self.send_head()
 45         if f:
 46             self.copyfile(f, self.wfile)
 47             f.close()
 48 
 49     def do_HEAD(self):
 50         """Serve a HEAD request."""
 51         f = self.send_head()
 52         if f:
 53             f.close()
 54 
 55     def send_head(self):
 56         """Common code for GET and HEAD commands.
 57 
 58         This sends the response code and MIME headers.
 59 
 60         Return value is either a file object (which has to be copied
 61         to the outputfile by the caller unless the command was HEAD,
 62         and must be closed by the caller under all circumstances), or
 63         None, in which case the caller has nothing further to do.
 64 
 65         """
 66         path = self.translate_path(self.path)
 67         f = None
 68         if os.path.isdir(path):
 69             if not self.path.endswith(\'/\'):
 70                 # redirect browser - doing basically what apache does
 71                 self.send_response(301)
 72                 self.send_header("Location", self.path + "/")
 73                 self.end_headers()
 74                 return None
 75             for index in "index.html", "index.htm":
 76                 index = os.path.join(path, index)
 77                 if os.path.exists(index):
 78                     path = index
 79                     break
 80             else:
 81                 return self.list_directory(path)
 82         ctype = self.guess_type(path)
 83         try:
 84             # Always read in binary mode. Opening files in text mode may cause
 85             # newline translations, making the actual size of the content
 86             # transmitted *less* than the content-length!
 87             f = open(path, \'rb\')
 88         except IOError:
 89             self.send_error(404, "File not found")
 90             return None
 91         self.send_response(200)
 92         self.send_header("Content-type", ctype)
 93         fs = os.fstat(f.fileno())
 94         self.send_header("Content-Length", str(fs[6]))
 95         self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
 96         self.end_headers()
 97         return f
 98 
 99     def list_directory(self, path):
100         """Helper to produce a directory listing (absent index.html).
101 
102         Return value is either a file object, or None (indicating an
103         error).  In either case, the headers are sent, making the
104         interface the same as for send_head().
105 
106         """
107         try:
108             list = os.listdir(path)
109         except os.error:
110             self.send_error(404, "No permission to list directory")
111             return None
112         list.sort(key=lambda a: a.lower())
113         f = StringIO()
114         displaypath = cgi.escape(urllib.unquote(self.path))
115         f.write(\'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\')
116         f.write("<html>\n<title>creditAutoTest project  %s</title>\n" % displaypath)
117         f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
118         f.write("<hr>\n<ul>\n")
119         for name in list:
120             fullname = os.path.join(path, name)
121             displayname = linkname = name
122             # Append / for directories or @ for symbolic links
123             if os.path.isdir(fullname):
124                 displayname = name + "/"
125                 linkname = name + "/"
126             if os.path.islink(fullname):
127                 displayname = name + "@"
128                 # Note: a link to a directory displays with @ and links with /
129             f.write(\'<li><a href="%s">%s</a>\n\'
130                     % (urllib.quote(linkname), cgi.escape(displayname)))
131         f.write("</ul>\n<hr>\n</body>\n</html>\n")
132         length = f.tell()
133         f.seek(0)
134         self.send_response(200)
135         encoding = sys.getfilesystemencoding()
136         self.send_header("Content-type", "text/html; charset=%s" % encoding)
137         self.send_header("Content-Length", str(length))
138         self.end_headers()
139         return f
140 
141     def translate_path(self, path):
142         """Translate a /-separated PATH to the local filename syntax.
143 
144         Components that mean special things to the local file system
145         (e.g. drive or directory names) are ignored.  (XXX They should
146         probably be diagnosed.)
147 
148         """
149         # abandon query parameters
150         path = path.split(\'?\',1)[0]
151         path = path.split(\'#\',1)[0]
152         path = posixpath.normpath(urllib.unquote(path))
153         words = path.split(\'/\')
154         words = filter(None, words)
155         path = os.getcwd()
156         for word in words:
157             drive, word = os.path.splitdrive(word)
158             head, word = os.path.split(word)
159             if word in (os.curdir, os.pardir): continue
160             path = os.path.join(path, word)
161         return path
162 
163     def copyfile(self, source, outputfile):
164         """Copy all data between two file objects.
165 
166         The SOURCE argument is a file object open for reading
167         (or anything with a read() method) and the DESTINATION
168         argument is a file object open for writing (or
169         anything with a write() method).
170 
171         The only reason for overriding this would be to change
172         the block size or perhaps to replace newlines by CRLF
173         -- note however that this the default server uses this
174         to copy binary data as well.
175 
176         """
177         shutil.copyfileobj(source, outputfile)
178 
179     def guess_type(self, path):
180         """Guess the type of a file.
181 
182         Argument is a PATH (a filename).
183 
184         Return value is a string of the form type/subtype,
185         usable for a MIME Content-type header.
186 
187         The default implementation looks the file\'s extension
188         up in the table self.extensions_map, using application/octet-stream
189         as a default; however it would be permissible (if
190         slow) to look inside the data to make a better guess.
191 
192         """
193 
194         base, ext = posixpath.splitext(path)
195         if ext in self.extensions_map:
196             return self.extensions_map[ext]
197         ext = ext.lower()
198         if ext in self.extensions_map:
199             return self.extensions_map[ext]
200         else:
201             return self.extensions_map[\'\']
202 
203     if not mimetypes.inited:
204         mimetypes.init() # try to read system mime.types
205     extensions_map = mimetypes.types_map.copy()
206     extensions_map.update({
207         \'\': \'application/octet-stream\', # Default
208         \'.py\': \'text/plain\',
209         \'.c\': \'text/plain\',
210         \'.h\': \'text/plain\',
211         \'.log\':\'text/plain\',
212         \'.out\':\'text/plain\',
213         \'.sql\':\'text/plain\',
214         \'.conf\':\'text/plain\'
215         })
216 
217 
218 def test(HandlerClass = SimpleHTTPRequestHandler,
219          ServerClass = BaseHTTPServer.HTTPServer):
220     BaseHTTPServer.test(HandlerClass, ServerClass)
221 
222 
223 if __name__ == \'__main__\':
224     test()
View Code

使用方式如下:

  1. 明确需要展示web的根目录,比如/user/src
  2. 了解SimpleHTTPServer模块的几个关键参数,只说一下web服务的启动端口port。比如python -m SimpleHTTPServer port
  3. 修改一下源码web说明和支撑多种文件格式等等:

 

最后,启动web服务器:

python -m SimpleHTTPServer 8009

 小建议,一般web服务都是常驻进程,这里建议也设置为常驻进程或者添加为linux系统服务。

  • 结果展示: