Updates of recent changes to logging.
[python.git] / Lib / SimpleHTTPServer.py
blob86c669ea409ed07b979a46bb75e39c6769136f02
1 """Simple HTTP Server.
3 This module builds on BaseHTTPServer by implementing the standard GET
4 and HEAD requests in a fairly straightforward manner.
6 """
9 __version__ = "0.6"
11 __all__ = ["SimpleHTTPRequestHandler"]
13 import os
14 import posixpath
15 import BaseHTTPServer
16 import urllib
17 import urlparse
18 import cgi
19 import shutil
20 import mimetypes
21 try:
22 from cStringIO import StringIO
23 except ImportError:
24 from StringIO import StringIO
27 class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
29 """Simple HTTP request handler with GET and HEAD commands.
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.
35 The GET and HEAD requests are identical except that the HEAD
36 request omits the actual contents of the file.
38 """
40 server_version = "SimpleHTTP/" + __version__
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()
49 def do_HEAD(self):
50 """Serve a HEAD request."""
51 f = self.send_head()
52 if f:
53 f.close()
55 def send_head(self):
56 """Common code for GET and HEAD commands.
58 This sends the response code and MIME headers.
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.
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 if ctype.startswith('text/'):
84 mode = 'r'
85 else:
86 mode = 'rb'
87 try:
88 f = open(path, mode)
89 except IOError:
90 self.send_error(404, "File not found")
91 return None
92 self.send_response(200)
93 self.send_header("Content-type", ctype)
94 fs = os.fstat(f.fileno())
95 self.send_header("Content-Length", str(fs[6]))
96 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
97 self.end_headers()
98 return f
100 def list_directory(self, path):
101 """Helper to produce a directory listing (absent index.html).
103 Return value is either a file object, or None (indicating an
104 error). In either case, the headers are sent, making the
105 interface the same as for send_head().
108 try:
109 list = os.listdir(path)
110 except os.error:
111 self.send_error(404, "No permission to list directory")
112 return None
113 list.sort(key=lambda a: a.lower())
114 f = StringIO()
115 displaypath = cgi.escape(urllib.unquote(self.path))
116 f.write("<title>Directory listing for %s</title>\n" % displaypath)
117 f.write("<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")
132 length = f.tell()
133 f.seek(0)
134 self.send_response(200)
135 self.send_header("Content-type", "text/html")
136 self.send_header("Content-Length", str(length))
137 self.end_headers()
138 return f
140 def translate_path(self, path):
141 """Translate a /-separated PATH to the local filename syntax.
143 Components that mean special things to the local file system
144 (e.g. drive or directory names) are ignored. (XXX They should
145 probably be diagnosed.)
148 # abandon query parameters
149 path = urlparse.urlparse(path)[2]
150 path = posixpath.normpath(urllib.unquote(path))
151 words = path.split('/')
152 words = filter(None, words)
153 path = os.getcwd()
154 for word in words:
155 drive, word = os.path.splitdrive(word)
156 head, word = os.path.split(word)
157 if word in (os.curdir, os.pardir): continue
158 path = os.path.join(path, word)
159 return path
161 def copyfile(self, source, outputfile):
162 """Copy all data between two file objects.
164 The SOURCE argument is a file object open for reading
165 (or anything with a read() method) and the DESTINATION
166 argument is a file object open for writing (or
167 anything with a write() method).
169 The only reason for overriding this would be to change
170 the block size or perhaps to replace newlines by CRLF
171 -- note however that this the default server uses this
172 to copy binary data as well.
175 shutil.copyfileobj(source, outputfile)
177 def guess_type(self, path):
178 """Guess the type of a file.
180 Argument is a PATH (a filename).
182 Return value is a string of the form type/subtype,
183 usable for a MIME Content-type header.
185 The default implementation looks the file's extension
186 up in the table self.extensions_map, using application/octet-stream
187 as a default; however it would be permissible (if
188 slow) to look inside the data to make a better guess.
192 base, ext = posixpath.splitext(path)
193 if ext in self.extensions_map:
194 return self.extensions_map[ext]
195 ext = ext.lower()
196 if ext in self.extensions_map:
197 return self.extensions_map[ext]
198 else:
199 return self.extensions_map['']
201 if not mimetypes.inited:
202 mimetypes.init() # try to read system mime.types
203 extensions_map = mimetypes.types_map.copy()
204 extensions_map.update({
205 '': 'application/octet-stream', # Default
206 '.py': 'text/plain',
207 '.c': 'text/plain',
208 '.h': 'text/plain',
212 def test(HandlerClass = SimpleHTTPRequestHandler,
213 ServerClass = BaseHTTPServer.HTTPServer):
214 BaseHTTPServer.test(HandlerClass, ServerClass)
217 if __name__ == '__main__':
218 test()