blob: d497e1e8cc797b9749bf74eab177ea2604dfe283 [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
Guido van Rossum57af0722000-05-09 14:57:09 +000017import cgi
Senthil Kumaran38535862011-03-17 12:34:18 +080018import sys
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:
Serhiy Storchaka1adbacf2014-01-25 19:42:27 +020046 try:
47 self.copyfile(f, self.wfile)
48 finally:
49 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000050
51 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000052 """Serve a HEAD request."""
53 f = self.send_head()
54 if f:
55 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000056
57 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000058 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000059
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000060 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000061
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000062 Return value is either a file object (which has to be copied
63 to the outputfile by the caller unless the command was HEAD,
64 and must be closed by the caller under all circumstances), or
65 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000066
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000067 """
68 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000069 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000070 if os.path.isdir(path):
Andrew M. Kuchling42819022006-12-22 19:06:16 +000071 if not self.path.endswith('/'):
72 # redirect browser - doing basically what apache does
73 self.send_response(301)
74 self.send_header("Location", self.path + "/")
75 self.end_headers()
76 return None
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000077 for index in "index.html", "index.htm":
78 index = os.path.join(path, index)
79 if os.path.exists(index):
80 path = index
81 break
82 else:
83 return self.list_directory(path)
84 ctype = self.guess_type(path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000085 try:
Amaury Forgeot d'Arc0f7cddc2008-07-06 21:34:39 +000086 # Always read in binary mode. Opening files in text mode may cause
87 # newline translations, making the actual size of the content
88 # transmitted *less* than the content-length!
89 f = open(path, 'rb')
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000090 except IOError:
91 self.send_error(404, "File not found")
92 return None
Serhiy Storchaka1adbacf2014-01-25 19:42:27 +020093 try:
94 self.send_response(200)
95 self.send_header("Content-type", ctype)
96 fs = os.fstat(f.fileno())
97 self.send_header("Content-Length", str(fs[6]))
98 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
99 self.end_headers()
100 return f
101 except:
102 f.close()
103 raise
Guido van Rossume7e578f1995-08-04 04:00:20 +0000104
Guido van Rossum57af0722000-05-09 14:57:09 +0000105 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000106 """Helper to produce a directory listing (absent index.html).
107
108 Return value is either a file object, or None (indicating an
109 error). In either case, the headers are sent, making the
110 interface the same as for send_head().
111
112 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000113 try:
114 list = os.listdir(path)
115 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000116 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000117 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000118 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000119 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000120 displaypath = cgi.escape(urllib.unquote(self.path))
Andrew M. Kuchlinga6298522008-02-28 14:03:03 +0000121 f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
122 f.write("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
123 f.write("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000124 f.write("<hr>\n<ul>\n")
125 for name in list:
126 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000127 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000128 # Append / for directories or @ for symbolic links
129 if os.path.isdir(fullname):
130 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000131 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000132 if os.path.islink(fullname):
133 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000134 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000135 f.write('<li><a href="%s">%s</a>\n'
136 % (urllib.quote(linkname), cgi.escape(displayname)))
Andrew M. Kuchlinga6298522008-02-28 14:03:03 +0000137 f.write("</ul>\n<hr>\n</body>\n</html>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000138 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000139 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000140 self.send_response(200)
Senthil Kumaran38535862011-03-17 12:34:18 +0800141 encoding = sys.getfilesystemencoding()
142 self.send_header("Content-type", "text/html; charset=%s" % encoding)
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000143 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000144 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000145 return f
146
Guido van Rossume7e578f1995-08-04 04:00:20 +0000147 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000148 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000149
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000150 Components that mean special things to the local file system
151 (e.g. drive or directory names) are ignored. (XXX They should
152 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000153
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000154 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000155 # abandon query parameters
Facundo Batistad14600e2008-02-18 12:48:43 +0000156 path = path.split('?',1)[0]
157 path = path.split('#',1)[0]
Senthil Kumarand4fac042013-09-13 00:18:55 -0700158 # Don't forget explicit trailing slash when normalizing. Issue17324
Senthil Kumarana719eed2013-09-29 18:57:42 -0700159 trailing_slash = path.rstrip().endswith('/')
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000160 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000161 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000162 words = filter(None, words)
163 path = os.getcwd()
164 for word in words:
165 drive, word = os.path.splitdrive(word)
166 head, word = os.path.split(word)
167 if word in (os.curdir, os.pardir): continue
168 path = os.path.join(path, word)
Senthil Kumarand4fac042013-09-13 00:18:55 -0700169 if trailing_slash:
170 path += '/'
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000171 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000172
173 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000174 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000175
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 The SOURCE argument is a file object open for reading
177 (or anything with a read() method) and the DESTINATION
178 argument is a file object open for writing (or
179 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000180
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000181 The only reason for overriding this would be to change
182 the block size or perhaps to replace newlines by CRLF
183 -- note however that this the default server uses this
184 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000185
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000186 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000187 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000188
189 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000190 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000191
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000192 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000193
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000194 Return value is a string of the form type/subtype,
195 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000196
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000197 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000198 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000199 as a default; however it would be permissible (if
200 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000201
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000202 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000203
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000204 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000205 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000206 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000207 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000208 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000209 return self.extensions_map[ext]
210 else:
211 return self.extensions_map['']
Tim Peters5d538b62006-06-14 22:38:13 +0000212
Georg Brandl83cc0d02006-06-14 08:50:03 +0000213 if not mimetypes.inited:
214 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000215 extensions_map = mimetypes.types_map.copy()
216 extensions_map.update({
217 '': 'application/octet-stream', # Default
218 '.py': 'text/plain',
219 '.c': 'text/plain',
220 '.h': 'text/plain',
221 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000222
223
224def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000225 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000226 BaseHTTPServer.test(HandlerClass, ServerClass)
227
228
229if __name__ == '__main__':
230 test()