blob: 9c87e66c2c9006ba3f10255577a96a1555456c75 [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 string
15import posixpath
Guido van Rossume7e578f1995-08-04 04:00:20 +000016import BaseHTTPServer
Guido van Rossumd7b147b1999-11-16 19:04:32 +000017import urllib
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 Rossum57af0722000-05-09 14:57:09 +000021from StringIO 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
29 subdirectories. It assumes that all files are plain text files
30 unless they have the extension ".html" in which case it assumes
31 they are HTML files.
32
33 The GET and HEAD requests are identical except that the HEAD
34 request omits the actual contents of the file.
35
36 """
37
38 server_version = "SimpleHTTP/" + __version__
39
40 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000041 """Serve a GET request."""
42 f = self.send_head()
43 if f:
44 self.copyfile(f, self.wfile)
45 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000046
47 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000048 """Serve a HEAD request."""
49 f = self.send_head()
50 if f:
51 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000052
53 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000054 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000055
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000056 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000057
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000058 Return value is either a file object (which has to be copied
59 to the outputfile by the caller unless the command was HEAD,
60 and must be closed by the caller under all circumstances), or
61 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000062
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000063 """
64 path = self.translate_path(self.path)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000065 f = None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000066 if os.path.isdir(path):
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000067 for index in "index.html", "index.htm":
68 index = os.path.join(path, index)
69 if os.path.exists(index):
70 path = index
71 break
72 else:
73 return self.list_directory(path)
74 ctype = self.guess_type(path)
75 if ctype.startswith('text/'):
76 mode = 'r'
Guido van Rossum57af0722000-05-09 14:57:09 +000077 else:
Guido van Rossum1d10f3e2000-05-21 16:25:29 +000078 mode = 'rb'
79 try:
80 f = open(path, mode)
81 except IOError:
82 self.send_error(404, "File not found")
83 return None
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000084 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000085 self.send_header("Content-type", ctype)
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")
119 f.seek(0)
Guido van Rossum1d10f3e2000-05-21 16:25:29 +0000120 self.send_response(200)
121 self.send_header("Content-type", "text/html")
122 self.end_headers()
Guido van Rossum57af0722000-05-09 14:57:09 +0000123 return f
124
Guido van Rossume7e578f1995-08-04 04:00:20 +0000125 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000126 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000127
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000128 Components that mean special things to the local file system
129 (e.g. drive or directory names) are ignored. (XXX They should
130 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000131
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000132 """
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000133 path = posixpath.normpath(urllib.unquote(path))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000134 words = string.splitfields(path, '/')
135 words = filter(None, words)
136 path = os.getcwd()
137 for word in words:
138 drive, word = os.path.splitdrive(word)
139 head, word = os.path.split(word)
140 if word in (os.curdir, os.pardir): continue
141 path = os.path.join(path, word)
142 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000143
144 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000145 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000146
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000147 The SOURCE argument is a file object open for reading
148 (or anything with a read() method) and the DESTINATION
149 argument is a file object open for writing (or
150 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000151
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000152 The only reason for overriding this would be to change
153 the block size or perhaps to replace newlines by CRLF
154 -- note however that this the default server uses this
155 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000156
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000157 """
Moshe Zadka37c03ff2000-07-29 05:15:56 +0000158 shutil.copyfileobj(source, outputfile)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000159
160 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000161 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000162
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000163 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000164
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000165 Return value is a string of the form type/subtype,
166 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000167
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000168 The default implementation looks the file's extension
169 up in the table self.extensions_map, using text/plain
170 as a default; however it would be permissible (if
171 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000172
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000173 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000174
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000175 base, ext = posixpath.splitext(path)
176 if self.extensions_map.has_key(ext):
177 return self.extensions_map[ext]
178 ext = string.lower(ext)
179 if self.extensions_map.has_key(ext):
180 return self.extensions_map[ext]
181 else:
182 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000183
Guido van Rossum077153e2001-01-14 23:21:25 +0000184 extensions_map = mimetypes.types_map.copy()
185 extensions_map.update({
186 '': 'application/octet-stream', # Default
187 '.py': 'text/plain',
188 '.c': 'text/plain',
189 '.h': 'text/plain',
190 })
Guido van Rossume7e578f1995-08-04 04:00:20 +0000191
192
193def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000194 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000195 BaseHTTPServer.test(HandlerClass, ServerClass)
196
197
198if __name__ == '__main__':
199 test()