blob: 2caa89dfe0c8cb9271bc5e59bc614eeb9162cf9e [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
Guido van Rossum57af0722000-05-09 14:57:09 +000020from StringIO import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000021
22
Guido van Rossume7e578f1995-08-04 04:00:20 +000023class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
24
25 """Simple HTTP request handler with GET and HEAD commands.
26
27 This serves files from the current directory and any of its
28 subdirectories. It assumes that all files are plain text files
29 unless they have the extension ".html" in which case it assumes
30 they are HTML files.
31
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):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000066 for index in "index.html", "index.htm":
67 index = os.path.join(path, index)
68 if os.path.exists(index):
69 path = index
70 break
71 else:
72 return self.list_directory(path)
73 ctype = self.guess_type(path)
74 if ctype.startswith('text/'):
75 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000076 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000077 mode = 'rb'
78 try:
79 f = open(path, mode)
80 except IOError:
81 self.send_error(404, "File not found")
82 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000083 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000084 self.send_header("Content-type", ctype)
Martin v. Löwis587c98c2002-03-17 18:37:22 +000085 self.send_header("Content-Length", str(os.fstat(f.fileno())[6]))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000086 self.end_headers()
87 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000088
Guido van Rossum57af0722000-05-09 14:57:09 +000089 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000090 """Helper to produce a directory listing (absent index.html).
91
92 Return value is either a file object, or None (indicating an
93 error). In either case, the headers are sent, making the
94 interface the same as for send_head().
95
96 """
Guido van Rossum57af0722000-05-09 14:57:09 +000097 try:
98 list = os.listdir(path)
99 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000100 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000101 return None
102 list.sort(lambda a, b: cmp(a.lower(), b.lower()))
103 f = StringIO()
Guido van Rossum1d105d12000-09-04 15:55:31 +0000104 f.write("<title>Directory listing for %s</title>\n" % self.path)
Guido van Rossum57af0722000-05-09 14:57:09 +0000105 f.write("<h2>Directory listing for %s</h2>\n" % self.path)
106 f.write("<hr>\n<ul>\n")
107 for name in list:
108 fullname = os.path.join(path, name)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000109 displayname = linkname = name = cgi.escape(name)
110 # Append / for directories or @ for symbolic links
111 if os.path.isdir(fullname):
112 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000113 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000114 if os.path.islink(fullname):
115 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000116 # Note: a link to a directory displays with @ and links with /
117 f.write('<li><a href="%s">%s</a>\n' % (linkname, displayname))
Guido van Rossum57af0722000-05-09 14:57:09 +0000118 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000119 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000120 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000121 self.send_response(200)
122 self.send_header("Content-type", "text/html")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000123 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000124 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000125 return f
126
Guido van Rossume7e578f1995-08-04 04:00:20 +0000127 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000128 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000129
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000130 Components that mean special things to the local file system
131 (e.g. drive or directory names) are ignored. (XXX They should
132 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000133
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000134 """
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000135 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000136 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000137 words = filter(None, words)
138 path = os.getcwd()
139 for word in words:
140 drive, word = os.path.splitdrive(word)
141 head, word = os.path.split(word)
142 if word in (os.curdir, os.pardir): continue
143 path = os.path.join(path, word)
144 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000145
146 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000147 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000148
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000149 The SOURCE argument is a file object open for reading
150 (or anything with a read() method) and the DESTINATION
151 argument is a file object open for writing (or
152 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000153
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000154 The only reason for overriding this would be to change
155 the block size or perhaps to replace newlines by CRLF
156 -- note however that this the default server uses this
157 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000158
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000159 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000160 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000161
162 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000163 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000164
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000165 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000166
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000167 Return value is a string of the form type/subtype,
168 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000169
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000170 The default implementation looks the file's extension
171 up in the table self.extensions_map, using text/plain
172 as a default; however it would be permissible (if
173 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000174
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000175 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000176
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000177 base, ext = posixpath.splitext(path)
178 if self.extensions_map.has_key(ext):
179 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000180 ext = ext.lower()
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000181 if self.extensions_map.has_key(ext):
182 return self.extensions_map[ext]
183 else:
184 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000185
Guido van Rossum077153e2001-01-14 23:21:25 +0000186 extensions_map = mimetypes.types_map.copy()
187 extensions_map.update({
188 '': 'application/octet-stream', # Default
189 '.py': 'text/plain',
190 '.c': 'text/plain',
191 '.h': 'text/plain',
192 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000193
194
195def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000196 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000197 BaseHTTPServer.test(HandlerClass, ServerClass)
198
199
200if __name__ == '__main__':
201 test()