blob: 286731d354952a6da25de9df29d21812c94b15cf [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
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:
47 self.copyfile(f, self.wfile)
48 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000049
50 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000051 """Serve a HEAD request."""
52 f = self.send_head()
53 if f:
54 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000055
56 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000057 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000058
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000059 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000060
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000061 Return value is either a file object (which has to be copied
62 to the outputfile by the caller unless the command was HEAD,
63 and must be closed by the caller under all circumstances), or
64 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000065
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000066 """
67 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000068 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000069 if os.path.isdir(path):
Andrew M. Kuchling60775f22006-12-22 19:08:41 +000070 if not self.path.endswith('/'):
71 # redirect browser - doing basically what apache does
72 self.send_response(301)
73 self.send_header("Location", self.path + "/")
74 self.end_headers()
75 return None
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000076 for index in "index.html", "index.htm":
77 index = os.path.join(path, index)
78 if os.path.exists(index):
79 path = index
80 break
81 else:
82 return self.list_directory(path)
83 ctype = self.guess_type(path)
84 if ctype.startswith('text/'):
85 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000086 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000087 mode = 'rb'
88 try:
89 f = open(path, mode)
90 except IOError:
91 self.send_error(404, "File not found")
92 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000093 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000094 self.send_header("Content-type", ctype)
Georg Brandl5d076962006-02-17 13:34:16 +000095 fs = os.fstat(f.fileno())
96 self.send_header("Content-Length", str(fs[6]))
97 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000098 self.end_headers()
99 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +0000100
Guido van Rossum57af0722000-05-09 14:57:09 +0000101 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000102 """Helper to produce a directory listing (absent index.html).
103
104 Return value is either a file object, or None (indicating an
105 error). In either case, the headers are sent, making the
106 interface the same as for send_head().
107
108 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000109 try:
110 list = os.listdir(path)
111 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000112 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000113 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000114 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000115 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000116 displaypath = cgi.escape(urllib.unquote(self.path))
117 f.write("<title>Directory listing for %s</title>\n" % displaypath)
118 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000119 f.write("<hr>\n<ul>\n")
120 for name in list:
121 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000122 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000123 # Append / for directories or @ for symbolic links
124 if os.path.isdir(fullname):
125 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000126 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000127 if os.path.islink(fullname):
128 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000129 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000130 f.write('<li><a href="%s">%s</a>\n'
131 % (urllib.quote(linkname), cgi.escape(displayname)))
Guido van Rossum57af0722000-05-09 14:57:09 +0000132 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000133 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000134 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000135 self.send_response(200)
Senthil Kumaran38535862011-03-17 12:34:18 +0800136 encoding = sys.getfilesystemencoding()
137 self.send_header("Content-type", "text/html; charset=%s" % encoding)
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000138 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000139 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000140 return f
141
Guido van Rossume7e578f1995-08-04 04:00:20 +0000142 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000143 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000144
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000145 Components that mean special things to the local file system
146 (e.g. drive or directory names) are ignored. (XXX They should
147 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000148
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000149 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000150 # abandon query parameters
151 path = urlparse.urlparse(path)[2]
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000152 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000153 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000154 words = filter(None, words)
155 path = os.getcwd()
156 for word in words:
157 drive, word = os.path.splitdrive(word)
158 head, word = os.path.split(word)
159 if word in (os.curdir, os.pardir): continue
160 path = os.path.join(path, word)
161 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000162
163 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000164 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000165
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000166 The SOURCE argument is a file object open for reading
167 (or anything with a read() method) and the DESTINATION
168 argument is a file object open for writing (or
169 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000170
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000171 The only reason for overriding this would be to change
172 the block size or perhaps to replace newlines by CRLF
173 -- note however that this the default server uses this
174 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000175
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000177 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000178
179 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000180 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000181
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000182 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000183
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000184 Return value is a string of the form type/subtype,
185 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000186
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000187 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000188 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000189 as a default; however it would be permissible (if
190 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000191
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000192 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000193
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000194 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000195 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000196 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000197 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000198 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000199 return self.extensions_map[ext]
200 else:
201 return self.extensions_map['']
Tim Peters5d538b62006-06-14 22:38:13 +0000202
Georg Brandl83cc0d02006-06-14 08:50:03 +0000203 if not mimetypes.inited:
204 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000205 extensions_map = mimetypes.types_map.copy()
206 extensions_map.update({
207 '': 'application/octet-stream', # Default
208 '.py': 'text/plain',
209 '.c': 'text/plain',
210 '.h': 'text/plain',
211 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000212
213
214def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000215 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000216 BaseHTTPServer.test(HandlerClass, ServerClass)
217
218
219if __name__ == '__main__':
220 test()