blob: 6def14e9dc28d1c5e2463d048ea5fea620af4712 [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 Rossum57af0722000-05-09 14:57:09 +00009__version__ = "0.4"
Guido van Rossume7e578f1995-08-04 04:00:20 +000010
11
12import os
Guido van Rossume7e578f1995-08-04 04:00:20 +000013import string
14import 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
18from StringIO import StringIO
Guido van Rossume7e578f1995-08-04 04:00:20 +000019
20
Guido van Rossume7e578f1995-08-04 04:00:20 +000021class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
22
23 """Simple HTTP request handler with GET and HEAD commands.
24
25 This serves files from the current directory and any of its
26 subdirectories. It assumes that all files are plain text files
27 unless they have the extension ".html" in which case it assumes
28 they are HTML files.
29
30 The GET and HEAD requests are identical except that the HEAD
31 request omits the actual contents of the file.
32
33 """
34
35 server_version = "SimpleHTTP/" + __version__
36
37 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000038 """Serve a GET request."""
39 f = self.send_head()
40 if f:
41 self.copyfile(f, self.wfile)
42 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000043
44 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000045 """Serve a HEAD request."""
46 f = self.send_head()
47 if f:
48 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000049
50 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000051 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000052
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000053 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000054
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000055 Return value is either a file object (which has to be copied
56 to the outputfile by the caller unless the command was HEAD,
57 and must be closed by the caller under all circumstances), or
58 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000059
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000060 """
61 path = self.translate_path(self.path)
62 if os.path.isdir(path):
Guido van Rossum57af0722000-05-09 14:57:09 +000063 f = self.list_directory(path)
64 if f is None:
65 return None
66 ctype = "text/HTML"
67 else:
68 try:
69 f = open(path, 'rb')
70 except IOError:
71 self.send_error(404, "File not found")
72 return None
73 ctype = self.guess_type(path)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 self.send_response(200)
Guido van Rossum57af0722000-05-09 14:57:09 +000075 self.send_header("Content-type", ctype)
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000076 self.end_headers()
77 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000078
Guido van Rossum57af0722000-05-09 14:57:09 +000079 def list_directory(self, path):
80 try:
81 list = os.listdir(path)
82 except os.error:
83 self.send_error(404, "No permission to list directory");
84 return None
85 list.sort(lambda a, b: cmp(a.lower(), b.lower()))
86 f = StringIO()
87 f.write("<h2>Directory listing for %s</h2>\n" % self.path)
88 f.write("<hr>\n<ul>\n")
89 for name in list:
90 fullname = os.path.join(path, name)
91 displayname = name = cgi.escape(name)
92 if os.path.islink(fullname):
93 displayname = name + "@"
94 elif os.path.isdir(fullname):
95 displayname = name + "/"
96 name = name + os.sep
97 f.write('<li><a href="%s">%s</a>\n' % (name, displayname))
98 f.write("</ul>\n<hr>\n")
99 f.seek(0)
100 return f
101
Guido van Rossume7e578f1995-08-04 04:00:20 +0000102 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000103 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000104
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000105 Components that mean special things to the local file system
106 (e.g. drive or directory names) are ignored. (XXX They should
107 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000108
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000109 """
Guido van Rossumd7b147b1999-11-16 19:04:32 +0000110 path = posixpath.normpath(urllib.unquote(path))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000111 words = string.splitfields(path, '/')
112 words = filter(None, words)
113 path = os.getcwd()
114 for word in words:
115 drive, word = os.path.splitdrive(word)
116 head, word = os.path.split(word)
117 if word in (os.curdir, os.pardir): continue
118 path = os.path.join(path, word)
119 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +0000120
121 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000122 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000123
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000124 The SOURCE argument is a file object open for reading
125 (or anything with a read() method) and the DESTINATION
126 argument is a file object open for writing (or
127 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000128
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000129 The only reason for overriding this would be to change
130 the block size or perhaps to replace newlines by CRLF
131 -- note however that this the default server uses this
132 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000133
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000134 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000135
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000136 BLOCKSIZE = 8192
137 while 1:
138 data = source.read(BLOCKSIZE)
139 if not data: break
140 outputfile.write(data)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000141
142 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000143 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000144
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000145 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000146
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000147 Return value is a string of the form type/subtype,
148 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000149
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000150 The default implementation looks the file's extension
151 up in the table self.extensions_map, using text/plain
152 as a default; however it would be permissible (if
153 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000154
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000155 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000156
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000157 base, ext = posixpath.splitext(path)
158 if self.extensions_map.has_key(ext):
159 return self.extensions_map[ext]
160 ext = string.lower(ext)
161 if self.extensions_map.has_key(ext):
162 return self.extensions_map[ext]
163 else:
164 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000165
166 extensions_map = {
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000167 '': 'text/plain', # Default, *must* be present
168 '.html': 'text/html',
169 '.htm': 'text/html',
170 '.gif': 'image/gif',
171 '.jpg': 'image/jpeg',
172 '.jpeg': 'image/jpeg',
173 }
Guido van Rossume7e578f1995-08-04 04:00:20 +0000174
175
176def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000177 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000178 BaseHTTPServer.test(HandlerClass, ServerClass)
179
180
181if __name__ == '__main__':
182 test()