blob: a4517dc0591bbf5170a345b3587c1e8baa12153a [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 Rossum3b7b8131995-09-18 21:50:43 +00009__version__ = "0.3"
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 Rossume7e578f1995-08-04 04:00:20 +000017
18
Guido van Rossume7e578f1995-08-04 04:00:20 +000019class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
20
21 """Simple HTTP request handler with GET and HEAD commands.
22
23 This serves files from the current directory and any of its
24 subdirectories. It assumes that all files are plain text files
25 unless they have the extension ".html" in which case it assumes
26 they are HTML files.
27
28 The GET and HEAD requests are identical except that the HEAD
29 request omits the actual contents of the file.
30
31 """
32
33 server_version = "SimpleHTTP/" + __version__
34
35 def do_GET(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000036 """Serve a GET request."""
37 f = self.send_head()
38 if f:
39 self.copyfile(f, self.wfile)
40 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000041
42 def do_HEAD(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000043 """Serve a HEAD request."""
44 f = self.send_head()
45 if f:
46 f.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +000047
48 def send_head(self):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000049 """Common code for GET and HEAD commands.
Guido van Rossume7e578f1995-08-04 04:00:20 +000050
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000051 This sends the response code and MIME headers.
Guido van Rossume7e578f1995-08-04 04:00:20 +000052
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000053 Return value is either a file object (which has to be copied
54 to the outputfile by the caller unless the command was HEAD,
55 and must be closed by the caller under all circumstances), or
56 None, in which case the caller has nothing further to do.
Guido van Rossume7e578f1995-08-04 04:00:20 +000057
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000058 """
59 path = self.translate_path(self.path)
60 if os.path.isdir(path):
61 self.send_error(403, "Directory listing not supported")
62 return None
63 try:
Guido van Rossum391c8b41998-12-07 03:53:18 +000064 f = open(path, 'rb')
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000065 except IOError:
66 self.send_error(404, "File not found")
67 return None
68 self.send_response(200)
69 self.send_header("Content-type", self.guess_type(path))
70 self.end_headers()
71 return f
Guido van Rossume7e578f1995-08-04 04:00:20 +000072
73 def translate_path(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 """Translate a /-separated PATH to the local filename syntax.
Guido van Rossume7e578f1995-08-04 04:00:20 +000075
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000076 Components that mean special things to the local file system
77 (e.g. drive or directory names) are ignored. (XXX They should
78 probably be diagnosed.)
Guido van Rossume7e578f1995-08-04 04:00:20 +000079
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000080 """
Guido van Rossumd7b147b1999-11-16 19:04:32 +000081 path = posixpath.normpath(urllib.unquote(path))
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000082 words = string.splitfields(path, '/')
83 words = filter(None, words)
84 path = os.getcwd()
85 for word in words:
86 drive, word = os.path.splitdrive(word)
87 head, word = os.path.split(word)
88 if word in (os.curdir, os.pardir): continue
89 path = os.path.join(path, word)
90 return path
Guido van Rossume7e578f1995-08-04 04:00:20 +000091
92 def copyfile(self, source, outputfile):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000093 """Copy all data between two file objects.
Guido van Rossume7e578f1995-08-04 04:00:20 +000094
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000095 The SOURCE argument is a file object open for reading
96 (or anything with a read() method) and the DESTINATION
97 argument is a file object open for writing (or
98 anything with a write() method).
Guido van Rossume7e578f1995-08-04 04:00:20 +000099
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000100 The only reason for overriding this would be to change
101 the block size or perhaps to replace newlines by CRLF
102 -- note however that this the default server uses this
103 to copy binary data as well.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000104
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000105 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000106
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000107 BLOCKSIZE = 8192
108 while 1:
109 data = source.read(BLOCKSIZE)
110 if not data: break
111 outputfile.write(data)
Guido van Rossume7e578f1995-08-04 04:00:20 +0000112
113 def guess_type(self, path):
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000114 """Guess the type of a file.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000115
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000116 Argument is a PATH (a filename).
Guido van Rossume7e578f1995-08-04 04:00:20 +0000117
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000118 Return value is a string of the form type/subtype,
119 usable for a MIME Content-type header.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000120
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000121 The default implementation looks the file's extension
122 up in the table self.extensions_map, using text/plain
123 as a default; however it would be permissible (if
124 slow) to look inside the data to make a better guess.
Guido van Rossume7e578f1995-08-04 04:00:20 +0000125
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000126 """
Guido van Rossume7e578f1995-08-04 04:00:20 +0000127
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000128 base, ext = posixpath.splitext(path)
129 if self.extensions_map.has_key(ext):
130 return self.extensions_map[ext]
131 ext = string.lower(ext)
132 if self.extensions_map.has_key(ext):
133 return self.extensions_map[ext]
134 else:
135 return self.extensions_map['']
Guido van Rossume7e578f1995-08-04 04:00:20 +0000136
137 extensions_map = {
Guido van Rossum45e2fbc1998-03-26 21:13:24 +0000138 '': 'text/plain', # Default, *must* be present
139 '.html': 'text/html',
140 '.htm': 'text/html',
141 '.gif': 'image/gif',
142 '.jpg': 'image/jpeg',
143 '.jpeg': 'image/jpeg',
144 }
Guido van Rossume7e578f1995-08-04 04:00:20 +0000145
146
147def test(HandlerClass = SimpleHTTPRequestHandler,
Guido van Rossum5c3b3841998-12-07 04:08:30 +0000148 ServerClass = BaseHTTPServer.HTTPServer):
Guido van Rossume7e578f1995-08-04 04:00:20 +0000149 BaseHTTPServer.test(HandlerClass, ServerClass)
150
151
152if __name__ == '__main__':
153 test()