0001 """Simple HTTP Server. 0002 0003 This module builds on BaseHTTPServer by implementing the standard GET 0004 and HEAD requests in a fairly straightforward manner. 0005 0006 """ 0007 0008 0009 __version__ = "0.6" 0010 0011 __all__ = ["SimpleHTTPRequestHandler"] 0012 0013 import os 0014 import posixpath 0015 import BaseHTTPServer 0016 import urllib 0017 import cgi 0018 import shutil 0019 import mimetypes 0020 from StringIO import StringIO 0021 0022 0023 class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 0024 0025 """Simple HTTP request handler with GET and HEAD commands. 0026 0027 This serves files from the current directory and any of its 0028 subdirectories. The MIME type for files is determined by 0029 calling the .guess_type() method. 0030 0031 The GET and HEAD requests are identical except that the HEAD 0032 request omits the actual contents of the file. 0033 0034 """ 0035 0036 server_version = "SimpleHTTP/" + __version__ 0037 0038 def do_GET(self): 0039 """Serve a GET request.""" 0040 f = self.send_head() 0041 if f: 0042 self.copyfile(f, self.wfile) 0043 f.close() 0044 0045 def do_HEAD(self): 0046 """Serve a HEAD request.""" 0047 f = self.send_head() 0048 if f: 0049 f.close() 0050 0051 def send_head(self): 0052 """Common code for GET and HEAD commands. 0053 0054 This sends the response code and MIME headers. 0055 0056 Return value is either a file object (which has to be copied 0057 to the outputfile by the caller unless the command was HEAD, 0058 and must be closed by the caller under all circumstances), or 0059 None, in which case the caller has nothing further to do. 0060 0061 """ 0062 path = self.translate_path(self.path) 0063 f = None 0064 if os.path.isdir(path): 0065 for index in "index.html", "index.htm": 0066 index = os.path.join(path, index) 0067 if os.path.exists(index): 0068 path = index 0069 break 0070 else: 0071 return self.list_directory(path) 0072 ctype = self.guess_type(path) 0073 if ctype.startswith('text/'): 0074 mode = 'r' 0075 else: 0076 mode = 'rb' 0077 try: 0078 f = open(path, mode) 0079 except IOError: 0080 self.send_error(404, "File not found") 0081 return None 0082 self.send_response(200) 0083 self.send_header("Content-type", ctype) 0084 self.send_header("Content-Length", str(os.fstat(f.fileno())[6])) 0085 self.end_headers() 0086 return f 0087 0088 def list_directory(self, path): 0089 """Helper to produce a directory listing (absent index.html). 0090 0091 Return value is either a file object, or None (indicating an 0092 error). In either case, the headers are sent, making the 0093 interface the same as for send_head(). 0094 0095 """ 0096 try: 0097 list = os.listdir(path) 0098 except os.error: 0099 self.send_error(404, "No permission to list directory") 0100 return None 0101 list.sort(key=lambda a: a.lower()) 0102 f = StringIO() 0103 f.write("<title>Directory listing for %s</title>\n" % self.path) 0104 f.write("<h2>Directory listing for %s</h2>\n" % self.path) 0105 f.write("<hr>\n<ul>\n") 0106 for name in list: 0107 fullname = os.path.join(path, name) 0108 displayname = linkname = name 0109 # Append / for directories or @ for symbolic links 0110 if os.path.isdir(fullname): 0111 displayname = name + "/" 0112 linkname = name + "/" 0113 if os.path.islink(fullname): 0114 displayname = name + "@" 0115 # Note: a link to a directory displays with @ and links with / 0116 f.write('<li><a href="%s">%s</a>\n' 0117 % (urllib.quote(linkname), cgi.escape(displayname))) 0118 f.write("</ul>\n<hr>\n") 0119 length = f.tell() 0120 f.seek(0) 0121 self.send_response(200) 0122 self.send_header("Content-type", "text/html") 0123 self.send_header("Content-Length", str(length)) 0124 self.end_headers() 0125 return f 0126 0127 def translate_path(self, path): 0128 """Translate a /-separated PATH to the local filename syntax. 0129 0130 Components that mean special things to the local file system 0131 (e.g. drive or directory names) are ignored. (XXX They should 0132 probably be diagnosed.) 0133 0134 """ 0135 path = posixpath.normpath(urllib.unquote(path)) 0136 words = path.split('/') 0137 words = filter(None, words) 0138 path = os.getcwd() 0139 for word in words: 0140 drive, word = os.path.splitdrive(word) 0141 head, word = os.path.split(word) 0142 if word in (os.curdir, os.pardir): continue 0143 path = os.path.join(path, word) 0144 return path 0145 0146 def copyfile(self, source, outputfile): 0147 """Copy all data between two file objects. 0148 0149 The SOURCE argument is a file object open for reading 0150 (or anything with a read() method) and the DESTINATION 0151 argument is a file object open for writing (or 0152 anything with a write() method). 0153 0154 The only reason for overriding this would be to change 0155 the block size or perhaps to replace newlines by CRLF 0156 -- note however that this the default server uses this 0157 to copy binary data as well. 0158 0159 """ 0160 shutil.copyfileobj(source, outputfile) 0161 0162 def guess_type(self, path): 0163 """Guess the type of a file. 0164 0165 Argument is a PATH (a filename). 0166 0167 Return value is a string of the form type/subtype, 0168 usable for a MIME Content-type header. 0169 0170 The default implementation looks the file's extension 0171 up in the table self.extensions_map, using application/octet-stream 0172 as a default; however it would be permissible (if 0173 slow) to look inside the data to make a better guess. 0174 0175 """ 0176 0177 base, ext = posixpath.splitext(path) 0178 if ext in self.extensions_map: 0179 return self.extensions_map[ext] 0180 ext = ext.lower() 0181 if ext in self.extensions_map: 0182 return self.extensions_map[ext] 0183 else: 0184 return self.extensions_map[''] 0185 0186 extensions_map = mimetypes.types_map.copy() 0187 extensions_map.update({ 0188 '': 'application/octet-stream', # Default 0189 '.py': 'text/plain', 0190 '.c': 'text/plain', 0191 '.h': 'text/plain', 0192 }) 0193 0194 0195 def test(HandlerClass = SimpleHTTPRequestHandler, 0196 ServerClass = BaseHTTPServer.HTTPServer): 0197 BaseHTTPServer.test(HandlerClass, ServerClass) 0198 0199 0200 if __name__ == '__main__': 0201 test() 0202
Generated by PyXR 0.9.4