blob: eea3243a727e09ba3c5f0274ddd847ba02fbf6a2 [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
Guido van Rossum68937b42007-05-18 00:51:22 +000021from io import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000022
23
Guido van Rossume7e578f1995-08-04 04:00:20 +000024class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
25
26 """Simple HTTP request handler with GET and HEAD commands.
27
28 This serves files from the current directory and any of its
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +000029 subdirectories. The MIME type for files is determined by
30 calling the .guess_type() method.
Guido van Rossume7e578f1995-08-04 04:00:20 +000031
32 The GET and HEAD requests are identical except that the HEAD
33 request omits the actual contents of the file.
34
35 """
36
37 server_version = "SimpleHTTP/" + __version__
38
39 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000040 """Serve a GET request."""
41 f = self.send_head()
42 if f:
43 self.copyfile(f, self.wfile)
44 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000045
46 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000047 """Serve a HEAD request."""
48 f = self.send_head()
49 if f:
50 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000051
52 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000053 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000054
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000055 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000056
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000057 Return value is either a file object (which has to be copied
58 to the outputfile by the caller unless the command was HEAD,
59 and must be closed by the caller under all circumstances), or
60 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000061
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000062 """
63 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000064 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000065 if os.path.isdir(path):
Thomas Wouters902d6eb2007-01-09 23:18:33 +000066 if not self.path.endswith('/'):
67 # redirect browser - doing basically what apache does
68 self.send_response(301)
69 self.send_header("Location", self.path + "/")
70 self.end_headers()
71 return None
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000072 for index in "index.html", "index.htm":
73 index = os.path.join(path, index)
74 if os.path.exists(index):
75 path = index
76 break
77 else:
78 return self.list_directory(path)
79 ctype = self.guess_type(path)
80 if ctype.startswith('text/'):
81 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000082 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000083 mode = 'rb'
84 try:
85 f = open(path, mode)
86 except IOError:
87 self.send_error(404, "File not found")
88 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000089 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000090 self.send_header("Content-type", ctype)
Georg Brandl5d076962006-02-17 13:34:16 +000091 fs = os.fstat(f.fileno())
92 self.send_header("Content-Length", str(fs[6]))
93 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000094 self.end_headers()
95 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000096
Guido van Rossum57af0722000-05-09 14:57:09 +000097 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000098 """Helper to produce a directory listing (absent index.html).
99
100 Return value is either a file object, or None (indicating an
101 error). In either case, the headers are sent, making the
102 interface the same as for send_head().
103
104 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000105 try:
106 list = os.listdir(path)
107 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000108 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000109 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000110 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000111 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000112 displaypath = cgi.escape(urllib.unquote(self.path))
113 f.write("<title>Directory listing for %s</title>\n" % displaypath)
114 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000115 f.write("<hr>\n<ul>\n")
116 for name in list:
117 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000118 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000119 # Append / for directories or @ for symbolic links
120 if os.path.isdir(fullname):
121 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000122 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000123 if os.path.islink(fullname):
124 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000125 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000126 f.write('<li><a href="%s">%s</a>\n'
127 % (urllib.quote(linkname), cgi.escape(displayname)))
Guido van Rossum57af0722000-05-09 14:57:09 +0000128 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000129 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000130 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000131 self.send_response(200)
132 self.send_header("Content-type", "text/html")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000133 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000134 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000135 return f
136
Guido van Rossume7e578f1995-08-04 04:00:20 +0000137 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000138 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000139
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000140 Components that mean special things to the local file system
141 (e.g. drive or directory names) are ignored. (XXX They should
142 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000143
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000144 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000145 # abandon query parameters
Christian Heimes7131fd92008-02-19 14:21:46 +0000146 path = path.split('?',1)[0]
147 path = path.split('#',1)[0]
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000148 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000149 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000150 words = filter(None, words)
151 path = os.getcwd()
152 for word in words:
153 drive, word = os.path.splitdrive(word)
154 head, word = os.path.split(word)
155 if word in (os.curdir, os.pardir): continue
156 path = os.path.join(path, word)
157 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000158
159 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000160 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000161
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000162 The SOURCE argument is a file object open for reading
163 (or anything with a read() method) and the DESTINATION
164 argument is a file object open for writing (or
165 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000166
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000167 The only reason for overriding this would be to change
168 the block size or perhaps to replace newlines by CRLF
169 -- note however that this the default server uses this
170 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000171
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000172 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000173 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000174
175 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000177
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000178 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000179
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000180 Return value is a string of the form type/subtype,
181 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000182
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000183 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000184 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000185 as a default; however it would be permissible (if
186 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000187
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000188 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000189
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000190 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000191 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000192 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000193 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000194 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000195 return self.extensions_map[ext]
196 else:
197 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000198
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000199 if not mimetypes.inited:
200 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000201 extensions_map = mimetypes.types_map.copy()
202 extensions_map.update({
203 '': 'application/octet-stream', # Default
204 '.py': 'text/plain',
205 '.c': 'text/plain',
206 '.h': 'text/plain',
207 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000208
209
210def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000211 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000212 BaseHTTPServer.test(HandlerClass, ServerClass)
213
214
215if __name__ == '__main__':
216 test()