基於python創建一個簡單的HTTP-WEB服務器


  • 背景

大多數情況下主機資源只有開發和測試相關人員可以登錄直接操作,且有些特定情況“答辯、演示、遠程”等這些場景下是無法直接登錄主機的。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系統服務。

  • 結果展示:

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM