Remove a ?? in the description of Mac OS support.
[python/dscho.git] / Lib / dos-8x3 / simpleht.py
blob4cfedbc9fe9ba8b1962fee713d163f4f6039ec2a
1 """Simple HTTP Server.
3 This module builds on BaseHTTPServer by implementing the standard GET
4 and HEAD requests in a fairly straightforward manner.
6 """
9 __version__ = "0.5"
12 import os
13 import string
14 import posixpath
15 import BaseHTTPServer
16 import urllib
17 import cgi
18 import shutil
19 from StringIO import StringIO
22 class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
24 """Simple HTTP request handler with GET and HEAD commands.
26 This serves files from the current directory and any of its
27 subdirectories. It assumes that all files are plain text files
28 unless they have the extension ".html" in which case it assumes
29 they are HTML files.
31 The GET and HEAD requests are identical except that the HEAD
32 request omits the actual contents of the file.
34 """
36 server_version = "SimpleHTTP/" + __version__
38 def do_GET(self):
39 """Serve a GET request."""
40 f = self.send_head()
41 if f:
42 self.copyfile(f, self.wfile)
43 f.close()
45 def do_HEAD(self):
46 """Serve a HEAD request."""
47 f = self.send_head()
48 if f:
49 f.close()
51 def send_head(self):
52 """Common code for GET and HEAD commands.
54 This sends the response code and MIME headers.
56 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.
61 """
62 path = self.translate_path(self.path)
63 f = None
64 if os.path.isdir(path):
65 for index in "index.html", "index.htm":
66 index = os.path.join(path, index)
67 if os.path.exists(index):
68 path = index
69 break
70 else:
71 return self.list_directory(path)
72 ctype = self.guess_type(path)
73 if ctype.startswith('text/'):
74 mode = 'r'
75 else:
76 mode = 'rb'
77 try:
78 f = open(path, mode)
79 except IOError:
80 self.send_error(404, "File not found")
81 return None
82 self.send_response(200)
83 self.send_header("Content-type", ctype)
84 self.end_headers()
85 return f
87 def list_directory(self, path):
88 """Helper to produce a directory listing (absent index.html).
90 Return value is either a file object, or None (indicating an
91 error). In either case, the headers are sent, making the
92 interface the same as for send_head().
94 """
95 try:
96 list = os.listdir(path)
97 except os.error:
98 self.send_error(404, "No permission to list directory");
99 return None
100 list.sort(lambda a, b: cmp(a.lower(), b.lower()))
101 f = StringIO()
102 f.write("<title>Directory listing for %s</title>\n" % self.path)
103 f.write("<h2>Directory listing for %s</h2>\n" % self.path)
104 f.write("<hr>\n<ul>\n")
105 for name in list:
106 fullname = os.path.join(path, name)
107 displayname = linkname = name = cgi.escape(name)
108 # Append / for directories or @ for symbolic links
109 if os.path.isdir(fullname):
110 displayname = name + "/"
111 linkname = name + "/"
112 if os.path.islink(fullname):
113 displayname = name + "@"
114 # Note: a link to a directory displays with @ and links with /
115 f.write('<li><a href="%s">%s</a>\n' % (linkname, displayname))
116 f.write("</ul>\n<hr>\n")
117 f.seek(0)
118 self.send_response(200)
119 self.send_header("Content-type", "text/html")
120 self.end_headers()
121 return f
123 def translate_path(self, path):
124 """Translate a /-separated PATH to the local filename syntax.
126 Components that mean special things to the local file system
127 (e.g. drive or directory names) are ignored. (XXX They should
128 probably be diagnosed.)
131 path = posixpath.normpath(urllib.unquote(path))
132 words = string.splitfields(path, '/')
133 words = filter(None, words)
134 path = os.getcwd()
135 for word in words:
136 drive, word = os.path.splitdrive(word)
137 head, word = os.path.split(word)
138 if word in (os.curdir, os.pardir): continue
139 path = os.path.join(path, word)
140 return path
142 def copyfile(self, source, outputfile):
143 """Copy all data between two file objects.
145 The SOURCE argument is a file object open for reading
146 (or anything with a read() method) and the DESTINATION
147 argument is a file object open for writing (or
148 anything with a write() method).
150 The only reason for overriding this would be to change
151 the block size or perhaps to replace newlines by CRLF
152 -- note however that this the default server uses this
153 to copy binary data as well.
156 shutil.copyfileobj(source, outputfile)
158 def guess_type(self, path):
159 """Guess the type of a file.
161 Argument is a PATH (a filename).
163 Return value is a string of the form type/subtype,
164 usable for a MIME Content-type header.
166 The default implementation looks the file's extension
167 up in the table self.extensions_map, using text/plain
168 as a default; however it would be permissible (if
169 slow) to look inside the data to make a better guess.
173 base, ext = posixpath.splitext(path)
174 if self.extensions_map.has_key(ext):
175 return self.extensions_map[ext]
176 ext = string.lower(ext)
177 if self.extensions_map.has_key(ext):
178 return self.extensions_map[ext]
179 else:
180 return self.extensions_map['']
182 extensions_map = {
183 '': 'text/plain', # Default, *must* be present
184 '.html': 'text/html',
185 '.htm': 'text/html',
186 '.gif': 'image/gif',
187 '.jpg': 'image/jpeg',
188 '.jpeg': 'image/jpeg',
192 def test(HandlerClass = SimpleHTTPRequestHandler,
193 ServerClass = BaseHTTPServer.HTTPServer):
194 BaseHTTPServer.test(HandlerClass, ServerClass)
197 if __name__ == '__main__':
198 test()