blob: c140a273fbe7090f1d6e251d56abc8c6b5cb8a99 [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
Benjamin Petersona71cfc52014-12-26 10:53:43 -060017import urlparse
Guido van Rossum57af0722000-05-09 14:57:09 +000018import cgi
Senthil Kumaran38535862011-03-17 12:34:18 +080019import sys
Moshe Zadka37c03ff2000-07-29 05:15:56 +000020import shutil
Guido van Rossum077153e2001-01-14 23:21:25 +000021import mimetypes
Raymond Hettingera6172712004-12-31 19:15:26 +000022try:
23 from cStringIO import StringIO
24except ImportError:
25 from StringIO import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000026
27
Guido van Rossume7e578f1995-08-04 04:00:20 +000028class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
29
30 """Simple HTTP request handler with GET and HEAD commands.
31
32 This serves files from the current directory and any of its
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +000033 subdirectories. The MIME type for files is determined by
34 calling the .guess_type() method.
Guido van Rossume7e578f1995-08-04 04:00:20 +000035
36 The GET and HEAD requests are identical except that the HEAD
37 request omits the actual contents of the file.
38
39 """
40
41 server_version = "SimpleHTTP/" + __version__
42
43 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000044 """Serve a GET request."""
45 f = self.send_head()
46 if f:
Serhiy Storchaka1adbacf2014-01-25 19:42:27 +020047 try:
48 self.copyfile(f, self.wfile)
49 finally:
50 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000051
52 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000053 """Serve a HEAD request."""
54 f = self.send_head()
55 if f:
56 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000057
58 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000059 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000060
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000061 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000062
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000063 Return value is either a file object (which has to be copied
64 to the outputfile by the caller unless the command was HEAD,
65 and must be closed by the caller under all circumstances), or
66 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000067
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000068 """
69 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000070 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000071 if os.path.isdir(path):
Benjamin Petersona71cfc52014-12-26 10:53:43 -060072 parts = urlparse.urlsplit(self.path)
73 if not parts.path.endswith('/'):
Andrew M. Kuchling42819022006-12-22 19:06:16 +000074 # redirect browser - doing basically what apache does
75 self.send_response(301)
Benjamin Petersona71cfc52014-12-26 10:53:43 -060076 new_parts = (parts[0], parts[1], parts[2] + '/',
77 parts[3], parts[4])
78 new_url = urlparse.urlunsplit(new_parts)
79 self.send_header("Location", new_url)
Andrew M. Kuchling42819022006-12-22 19:06:16 +000080 self.end_headers()
81 return None
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000082 for index in "index.html", "index.htm":
83 index = os.path.join(path, index)
84 if os.path.exists(index):
85 path = index
86 break
87 else:
88 return self.list_directory(path)
89 ctype = self.guess_type(path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000090 try:
Amaury Forgeot d'Arc0f7cddc2008-07-06 21:34:39 +000091 # Always read in binary mode. Opening files in text mode may cause
92 # newline translations, making the actual size of the content
93 # transmitted *less* than the content-length!
94 f = open(path, 'rb')
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000095 except IOError:
96 self.send_error(404, "File not found")
97 return None
Serhiy Storchaka1adbacf2014-01-25 19:42:27 +020098 try:
99 self.send_response(200)
100 self.send_header("Content-type", ctype)
101 fs = os.fstat(f.fileno())
102 self.send_header("Content-Length", str(fs[6]))
103 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
104 self.end_headers()
105 return f
106 except:
107 f.close()
108 raise
Guido van Rossume7e578f1995-08-04 04:00:20 +0000109
Guido van Rossum57af0722000-05-09 14:57:09 +0000110 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000111 """Helper to produce a directory listing (absent index.html).
112
113 Return value is either a file object, or None (indicating an
114 error). In either case, the headers are sent, making the
115 interface the same as for send_head().
116
117 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000118 try:
119 list = os.listdir(path)
120 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000121 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000122 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000123 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000124 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000125 displaypath = cgi.escape(urllib.unquote(self.path))
Andrew M. Kuchlinga6298522008-02-28 14:03:03 +0000126 f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
127 f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
128 f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000129 f.write("<hr>\n<ul>\n")
130 for name in list:
131 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000132 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000133 # Append / for directories or @ for symbolic links
134 if os.path.isdir(fullname):
135 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000136 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000137 if os.path.islink(fullname):
138 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000139 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000140 f.write('<li><a href="%s">%s</a>\n'
141 % (urllib.quote(linkname), cgi.escape(displayname)))
Andrew M. Kuchlinga6298522008-02-28 14:03:03 +0000142 f.write("</ul>\n<hr>\n</body>\n</html>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000143 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000144 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000145 self.send_response(200)
Senthil Kumaran38535862011-03-17 12:34:18 +0800146 encoding = sys.getfilesystemencoding()
147 self.send_header("Content-type", "text/html; charset=%s" % encoding)
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000148 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000149 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000150 return f
151
Guido van Rossume7e578f1995-08-04 04:00:20 +0000152 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000153 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000154
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000155 Components that mean special things to the local file system
156 (e.g. drive or directory names) are ignored. (XXX They should
157 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000158
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000159 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000160 # abandon query parameters
Facundo Batistad14600e2008-02-18 12:48:43 +0000161 path = path.split('?',1)[0]
162 path = path.split('#',1)[0]
Senthil Kumarand4fac042013-09-13 00:18:55 -0700163 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumarana719eed2013-09-29 18:57:42 -0700164 trailing_slash = path.rstrip().endswith('/')
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000165 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000166 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000167 words = filter(None, words)
168 path = os.getcwd()
169 for word in words:
Martin Panter0cf2cf22016-04-18 03:45:18 +0000170 if os.path.dirname(word) or word in (os.curdir, os.pardir):
171 # Ignore components that are not a simple file/directory name
172 continue
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000173 path = os.path.join(path, word)
Senthil Kumarand4fac042013-09-13 00:18:55 -0700174 if trailing_slash:
175 path += '/'
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000177
178 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000179 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000180
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000181 The SOURCE argument is a file object open for reading
182 (or anything with a read() method) and the DESTINATION
183 argument is a file object open for writing (or
184 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000185
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000186 The only reason for overriding this would be to change
187 the block size or perhaps to replace newlines by CRLF
188 -- note however that this the default server uses this
189 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000190
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000191 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000192 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000193
194 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000195 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000196
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000197 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000198
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000199 Return value is a string of the form type/subtype,
200 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000201
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000202 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000203 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000204 as a default; however it would be permissible (if
205 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000206
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000207 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000208
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000209 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000210 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000211 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000212 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000213 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000214 return self.extensions_map[ext]
215 else:
216 return self.extensions_map['']
Tim Peters5d538b62006-06-14 22:38:13 +0000217
Georg Brandl83cc0d02006-06-14 08:50:03 +0000218 if not mimetypes.inited:
219 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000220 extensions_map = mimetypes.types_map.copy()
221 extensions_map.update({
222 '': 'application/octet-stream', # Default
223 '.py': 'text/plain',
224 '.c': 'text/plain',
225 '.h': 'text/plain',
226 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000227
228
229def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000230 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000231 BaseHTTPServer.test(HandlerClass, ServerClass)
232
233
234if __name__ == '__main__':
235 test()