blob: eac95cab96a774a873e6978a667d9b91e8256c5a [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
Moshe Zadka37c03ff2000-07-29 05:15:56 +000018import shutil
Guido van Rossum077153e2001-01-14 23:21:25 +000019import mimetypes
Raymond Hettingera6172712004-12-31 19:15:26 +000020try:
21 from cStringIO import StringIO
22except ImportError:
23 from StringIO import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000024
25
Guido van Rossume7e578f1995-08-04 04:00:20 +000026class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
27
28 """Simple HTTP request handler with GET and HEAD commands.
29
30 This serves files from the current directory and any of its
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +000031 subdirectories. The MIME type for files is determined by
32 calling the .guess_type() method.
Guido van Rossume7e578f1995-08-04 04:00:20 +000033
34 The GET and HEAD requests are identical except that the HEAD
35 request omits the actual contents of the file.
36
37 """
38
39 server_version = "SimpleHTTP/" + __version__
40
41 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000042 """Serve a GET request."""
43 f = self.send_head()
44 if f:
45 self.copyfile(f, self.wfile)
46 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000047
48 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000049 """Serve a HEAD request."""
50 f = self.send_head()
51 if f:
52 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000053
54 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000055 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000056
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000057 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000058
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000059 Return value is either a file object (which has to be copied
60 to the outputfile by the caller unless the command was HEAD,
61 and must be closed by the caller under all circumstances), or
62 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000063
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000064 """
65 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000066 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000067 if os.path.isdir(path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000068 for index in "index.html", "index.htm":
69 index = os.path.join(path, index)
70 if os.path.exists(index):
71 path = index
72 break
73 else:
74 return self.list_directory(path)
75 ctype = self.guess_type(path)
76 if ctype.startswith('text/'):
77 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000078 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000079 mode = 'rb'
80 try:
81 f = open(path, mode)
82 except IOError:
83 self.send_error(404, "File not found")
84 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000085 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000086 self.send_header("Content-type", ctype)
Martin v. Löwis587c98c2002-03-17 18:37:22 +000087 self.send_header("Content-Length", str(os.fstat(f.fileno())[6]))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000088 self.end_headers()
89 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000090
Guido van Rossum57af0722000-05-09 14:57:09 +000091 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000092 """Helper to produce a directory listing (absent index.html).
93
94 Return value is either a file object, or None (indicating an
95 error). In either case, the headers are sent, making the
96 interface the same as for send_head().
97
98 """
Guido van Rossum57af0722000-05-09 14:57:09 +000099 try:
100 list = os.listdir(path)
101 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000102 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000103 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000104 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000105 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000106 displaypath = cgi.escape(urllib.unquote(self.path))
107 f.write("<title>Directory listing for %s</title>\n" % displaypath)
108 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000109 f.write("<hr>\n<ul>\n")
110 for name in list:
111 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000112 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000113 # Append / for directories or @ for symbolic links
114 if os.path.isdir(fullname):
115 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000116 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000117 if os.path.islink(fullname):
118 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000119 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000120 f.write('<li><a href="%s">%s</a>\n'
121 % (urllib.quote(linkname), cgi.escape(displayname)))
Guido van Rossum57af0722000-05-09 14:57:09 +0000122 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000123 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000124 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000125 self.send_response(200)
126 self.send_header("Content-type", "text/html")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000127 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000128 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000129 return f
130
Guido van Rossume7e578f1995-08-04 04:00:20 +0000131 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000132 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000133
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000134 Components that mean special things to the local file system
135 (e.g. drive or directory names) are ignored. (XXX They should
136 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000137
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000138 """
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000139 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000140 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000141 words = filter(None, words)
142 path = os.getcwd()
143 for word in words:
144 drive, word = os.path.splitdrive(word)
145 head, word = os.path.split(word)
146 if word in (os.curdir, os.pardir): continue
147 path = os.path.join(path, word)
148 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000149
150 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000151 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000152
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000153 The SOURCE argument is a file object open for reading
154 (or anything with a read() method) and the DESTINATION
155 argument is a file object open for writing (or
156 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000157
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000158 The only reason for overriding this would be to change
159 the block size or perhaps to replace newlines by CRLF
160 -- note however that this the default server uses this
161 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000162
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000163 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000164 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000165
166 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000167 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000168
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000169 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000170
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000171 Return value is a string of the form type/subtype,
172 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000173
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000174 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000175 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000176 as a default; however it would be permissible (if
177 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000178
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000179 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000180
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000181 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000182 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000183 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000184 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000185 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000186 return self.extensions_map[ext]
187 else:
188 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000189
Guido van Rossum077153e2001-01-14 23:21:25 +0000190 extensions_map = mimetypes.types_map.copy()
191 extensions_map.update({
192 '': 'application/octet-stream', # Default
193 '.py': 'text/plain',
194 '.c': 'text/plain',
195 '.h': 'text/plain',
196 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000197
198
199def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000200 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000201 BaseHTTPServer.test(HandlerClass, ServerClass)
202
203
204if __name__ == '__main__':
205 test()