blob: deedd4b77221ac142c4b60edbb7d01a38fda4550 [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 Rossum68937b42007-05-18 00:51:22 +000020from io 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
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +000028 subdirectories. The MIME type for files is determined by
29 calling the .guess_type() method.
Guido van Rossume7e578f1995-08-04 04:00:20 +000030
31 The GET and HEAD requests are identical except that the HEAD
32 request omits the actual contents of the file.
33
34 """
35
36 server_version = "SimpleHTTP/" + __version__
37
38 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000039 """Serve a GET request."""
40 f = self.send_head()
41 if f:
42 self.copyfile(f, self.wfile)
43 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000044
45 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000046 """Serve a HEAD request."""
47 f = self.send_head()
48 if f:
49 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000050
51 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000052 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000053
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000054 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000055
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000056 Return value is either a file object (which has to be copied
57 to the outputfile by the caller unless the command was HEAD,
58 and must be closed by the caller under all circumstances), or
59 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000060
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000061 """
62 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000063 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000064 if os.path.isdir(path):
Thomas Wouters902d6eb2007-01-09 23:18:33 +000065 if not self.path.endswith('/'):
66 # redirect browser - doing basically what apache does
67 self.send_response(301)
68 self.send_header("Location", self.path + "/")
69 self.end_headers()
70 return None
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000071 for index in "index.html", "index.htm":
72 index = os.path.join(path, index)
73 if os.path.exists(index):
74 path = index
75 break
76 else:
77 return self.list_directory(path)
78 ctype = self.guess_type(path)
79 if ctype.startswith('text/'):
80 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000081 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000082 mode = 'rb'
83 try:
84 f = open(path, mode)
85 except IOError:
86 self.send_error(404, "File not found")
87 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000088 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000089 self.send_header("Content-type", ctype)
Georg Brandl5d076962006-02-17 13:34:16 +000090 fs = os.fstat(f.fileno())
91 self.send_header("Content-Length", str(fs[6]))
92 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000093 self.end_headers()
94 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000095
Guido van Rossum57af0722000-05-09 14:57:09 +000096 def list_directory(self, path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000097 """Helper to produce a directory listing (absent index.html).
98
99 Return value is either a file object, or None (indicating an
100 error). In either case, the headers are sent, making the
101 interface the same as for send_head().
102
103 """
Guido van Rossum57af0722000-05-09 14:57:09 +0000104 try:
105 list = os.listdir(path)
106 except os.error:
Jeremy Hylton5b48c452001-01-26 17:08:32 +0000107 self.send_error(404, "No permission to list directory")
Guido van Rossum57af0722000-05-09 14:57:09 +0000108 return None
Raymond Hettinger6b59f5f2003-10-16 05:53:16 +0000109 list.sort(key=lambda a: a.lower())
Guido van Rossum57af0722000-05-09 14:57:09 +0000110 f = StringIO()
Georg Brandl6ee69522005-12-16 19:36:08 +0000111 displaypath = cgi.escape(urllib.unquote(self.path))
112 f.write("<title>Directory listing for %s</title>\n" % displaypath)
113 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
Guido van Rossum57af0722000-05-09 14:57:09 +0000114 f.write("<hr>\n<ul>\n")
115 for name in list:
116 fullname = os.path.join(path, name)
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000117 displayname = linkname = name
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000118 # Append / for directories or @ for symbolic links
119 if os.path.isdir(fullname):
120 displayname = name + "/"
Guido van Rossum1d105d12000-09-04 15:55:31 +0000121 linkname = name + "/"
Guido van Rossum57af0722000-05-09 14:57:09 +0000122 if os.path.islink(fullname):
123 displayname = name + "@"
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000124 # Note: a link to a directory displays with @ and links with /
Johannes Gijsbers6d63a8d2004-08-21 10:43:29 +0000125 f.write('<li><a href="%s">%s</a>\n'
126 % (urllib.quote(linkname), cgi.escape(displayname)))
Guido van Rossum57af0722000-05-09 14:57:09 +0000127 f.write("</ul>\n<hr>\n")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000128 length = f.tell()
Guido van Rossum57af0722000-05-09 14:57:09 +0000129 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000130 self.send_response(200)
131 self.send_header("Content-type", "text/html")
Martin v. Löwis587c98c2002-03-17 18:37:22 +0000132 self.send_header("Content-Length", str(length))
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000133 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000134 return f
135
Guido van Rossume7e578f1995-08-04 04:00:20 +0000136 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000137 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000138
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000139 Components that mean special things to the local file system
140 (e.g. drive or directory names) are ignored. (XXX They should
141 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000142
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000143 """
Georg Brandl45ab2332006-01-13 17:05:56 +0000144 # abandon query parameters
Christian Heimes7131fd92008-02-19 14:21:46 +0000145 path = path.split('?',1)[0]
146 path = path.split('#',1)[0]
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000147 path = posixpath.normpath(urllib.unquote(path))
Eric S. Raymond304b6a32001-02-09 10:26:06 +0000148 words = path.split('/')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000149 words = filter(None, words)
150 path = os.getcwd()
151 for word in words:
152 drive, word = os.path.splitdrive(word)
153 head, word = os.path.split(word)
154 if word in (os.curdir, os.pardir): continue
155 path = os.path.join(path, word)
156 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000157
158 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000159 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000160
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000161 The SOURCE argument is a file object open for reading
162 (or anything with a read() method) and the DESTINATION
163 argument is a file object open for writing (or
164 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000165
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000166 The only reason for overriding this would be to change
167 the block size or perhaps to replace newlines by CRLF
168 -- note however that this the default server uses this
169 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000170
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000171 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000172 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000173
174 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000175 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000176
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000177 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000178
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000179 Return value is a string of the form type/subtype,
180 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000181
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000182 The default implementation looks the file's extension
Andrew M. Kuchlingb839c1f2004-08-07 19:02:19 +0000183 up in the table self.extensions_map, using application/octet-stream
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000184 as a default; however it would be permissible (if
185 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000186
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000187 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000188
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000189 base, ext = posixpath.splitext(path)
Raymond Hettinger54f02222002-06-01 14:18:47 +0000190 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000191 return self.extensions_map[ext]
Eric S. Raymondbf97c9d2001-02-09 10:18:37 +0000192 ext = ext.lower()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000193 if ext in self.extensions_map:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000194 return self.extensions_map[ext]
195 else:
196 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000197
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000198 if not mimetypes.inited:
199 mimetypes.init() # try to read system mime.types
Guido van Rossum077153e2001-01-14 23:21:25 +0000200 extensions_map = mimetypes.types_map.copy()
201 extensions_map.update({
202 '': 'application/octet-stream', # Default
203 '.py': 'text/plain',
204 '.c': 'text/plain',
205 '.h': 'text/plain',
206 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000207
208
209def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000210 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000211 BaseHTTPServer.test(HandlerClass, ServerClass)
212
213
214if __name__ == '__main__':
215 test()