blob: 6f348a1097f3e901c141cdf5765de2797bff1152 [file] [log] [blame]
Guido van Rossume7e578f1995-08-04 04:00:20 +00001"""Simple HTTP Server.
2
3This module builds on BaseHTTPServer by implementing the standard GET
4and HEAD requests in a fairly straightforward manner.
5
6"""
7
8
Guido van Rossum077153e2001-01-14 23:21:25 +00009__version__ = "0.6"
Guido van Rossume7e578f1995-08-04 04:00:20 +000010
Skip Montanaroe99d5ea2001-01-20 19:54:20 +000011__all__ = ["SimpleHTTPRequestHandler"]
Guido van Rossume7e578f1995-08-04 04:00:20 +000012
13import os
Guido van Rossume7e578f1995-08-04 04:00:20 +000014import posixpath
Guido van Rossume7e578f1995-08-04 04:00:20 +000015import BaseHTTPServer
Guido van Rossumd7b147b1999-11-16 19:04:32 +000016import urllib
Georg Brandl45ab2332006-01-13 17:05:56 +000017import urlparse
Guido van Rossum57af0722000-05-09 14:57:09 +000018import cgi
Moshe Zadka37c03ff2000-07-29 05:15:56 +000019import shutil
Guido van Rossum077153e2001-01-14 23:21:25 +000020import mimetypes
Raymond Hettingera6172712004-12-31 19:15:26 +000021try:
22 from cStringIO import StringIO
23except ImportError:
24 from StringIO import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000025
26
Guido van Rossume7e578f1995-08-04 04:00:20 +000027class 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
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +000032 subdirectories. The MIME type for files is determined by
33 calling the .guess_type() method.
Guido van Rossume7e578f1995-08-04 04:00:20 +000034
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):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000043 """Serve a GET request."""
44 f = self.send_head()
45 if f:
46 self.copyfile(f, self.wfile)
47 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000048
49 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000050 """Serve a HEAD request."""
51 f = self.send_head()
52 if f:
53 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000054
55 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000056 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000057
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000058 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000059
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000060 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.
Guido van Rossume7e578f1995-08-04 04:00:20 +000064
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000065 """
66 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000067 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000068 if os.path.isdir(path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000069 for index in "index.html", "index.htm":
70 index = os.path.join(path, index)
71 if os.path.exists(index):
72 path = index
73 break
74 else:
75 return self.list_directory(path)
76 ctype = self.guess_type(path)
77 if ctype.startswith('text/'):
78 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000079 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000080 mode = 'rb'
81 try:
82 f = open(path, mode)
83 except IOError:
84 self.send_error(404, "File not found")
85 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000086 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000087 self.send_header("Content-type", ctype)
Georg Brandl5d076962006-02-17 13:34:16 +000088 fs = os.fstat(f.fileno())
89 self.send_header("Content-Length", str(fs[6]))
90 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000091 self.end_headers()
92 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000093
Guido van Rossum57af0722000-05-09 14:57:09 +000094 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000095 """Helper to produce a directory listing (absent index.html).
96
97 Return value is either a file object, or None (indicating an
98 error). In either case, the headers are sent, making the
99 interface the same as for send_head().
100
101 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000102 try:
103 list = os.listdir(path)
104 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000105 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000106 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000107 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000108 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000109 displaypath = cgi.escape(urllib.unquote(self.path))
110 f.write("<title>Directory listing for %s</title>\n" % displaypath)
111 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000112 f.write("<hr>\n<ul>\n")
113 for name in list:
114 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000115 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000116 # Append / for directories or @ for symbolic links
117 if os.path.isdir(fullname):
118 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000119 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000120 if os.path.islink(fullname):
121 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000122 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000123 f.write('<li><a href="%s">%s</a>\n'
124 % (urllib.quote(linkname), cgi.escape(displayname)))
Guido van Rossum57af0722000-05-09 14:57:09 +0000125 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000126 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000127 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000128 self.send_response(200)
129 self.send_header("Content-type", "text/html")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000130 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000131 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000132 return f
133
Guido van Rossume7e578f1995-08-04 04:00:20 +0000134 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000135 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000136
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000137 Components that mean special things to the local file system
138 (e.g. drive or directory names) are ignored. (XXX They should
139 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000140
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000141 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000142 # abandon query parameters
143 path = urlparse.urlparse(path)[2]
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000144 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000145 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000146 words = filter(None, words)
147 path = os.getcwd()
148 for word in words:
149 drive, word = os.path.splitdrive(word)
150 head, word = os.path.split(word)
151 if word in (os.curdir, os.pardir): continue
152 path = os.path.join(path, word)
153 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000154
155 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000156 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000157
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000158 The SOURCE argument is a file object open for reading
159 (or anything with a read() method) and the DESTINATION
160 argument is a file object open for writing (or
161 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000162
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000163 The only reason for overriding this would be to change
164 the block size or perhaps to replace newlines by CRLF
165 -- note however that this the default server uses this
166 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000167
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000168 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000169 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000170
171 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000172 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000173
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000174 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000175
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 Return value is a string of the form type/subtype,
177 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000178
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000179 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000180 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000181 as a default; however it would be permissible (if
182 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000183
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000184 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000185
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000186 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000187 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000188 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000189 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000190 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000191 return self.extensions_map[ext]
192 else:
193 return self.extensions_map['']
Georg Brandl83cc0d02006-06-14 08:50:03 +0000194
195 if not mimetypes.inited:
196 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000197 extensions_map = mimetypes.types_map.copy()
198 extensions_map.update({
199 '': 'application/octet-stream', # Default
200 '.py': 'text/plain',
201 '.c': 'text/plain',
202 '.h': 'text/plain',
203 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000204
205
206def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000207 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000208 BaseHTTPServer.test(HandlerClass, ServerClass)
209
210
211if __name__ == '__main__':
212 test()