source: trunk/essentials/dev-lang/python/Lib/SimpleHTTPServer.py@ 3393

Last change on this file since 3393 was 3225, checked in by bird, 19 years ago

Python 2.5

File size: 6.7 KB
Line 
1"""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
9__version__ = "0.6"
10
11__all__ = ["SimpleHTTPRequestHandler"]
12
13import os
14import posixpath
15import BaseHTTPServer
16import urllib
17import urlparse
18import cgi
19import shutil
20import mimetypes
21try:
22 from cStringIO import StringIO
23except ImportError:
24 from StringIO import StringIO
25
26
27class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
28
29 """Simple HTTP request handler with GET and HEAD commands.
30
31 This serves files from the current directory and any of its
32 subdirectories. The MIME type for files is determined by
33 calling the .guess_type() method.
34
35 The GET and HEAD requests are identical except that the HEAD
36 request omits the actual contents of the file.
37
38 """
39
40 server_version = "SimpleHTTP/" + __version__
41
42 def do_GET(self):
43 """Serve a GET request."""
44 f = self.send_head()
45 if f:
46 self.copyfile(f, self.wfile)
47 f.close()
48
49 def do_HEAD(self):
50 """Serve a HEAD request."""
51 f = self.send_head()
52 if f:
53 f.close()
54
55 def send_head(self):
56 """Common code for GET and HEAD commands.
57
58 This sends the response code and MIME headers.
59
60 Return value is either a file object (which has to be copied
61 to the outputfile by the caller unless the command was HEAD,
62 and must be closed by the caller under all circumstances), or
63 None, in which case the caller has nothing further to do.
64
65 """
66 path = self.translate_path(self.path)
67 f = None
68 if os.path.isdir(path):
69 for index in "index.html", "index.htm":
70 index = os.path.join(path, index)
71 if os.path.exists(index):
72 path = index
73 break
74 else:
75 return self.list_directory(path)
76 ctype = self.guess_type(path)
77 if ctype.startswith('text/'):
78 mode = 'r'
79 else:
80 mode = 'rb'
81 try:
82 f = open(path, mode)
83 except IOError:
84 self.send_error(404, "File not found")
85 return None
86 self.send_response(200)
87 self.send_header("Content-type", ctype)
88 fs = os.fstat(f.fileno())
89 self.send_header("Content-Length", str(fs[6]))
90 self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
91 self.end_headers()
92 return f
93
94 def list_directory(self, path):
95 """Helper to produce a directory listing (absent index.html).
96
97 Return value is either a file object, or None (indicating an
98 error). In either case, the headers are sent, making the
99 interface the same as for send_head().
100
101 """
102 try:
103 list = os.listdir(path)
104 except os.error:
105 self.send_error(404, "No permission to list directory")
106 return None
107 list.sort(key=lambda a: a.lower())
108 f = StringIO()
109 displaypath = cgi.escape(urllib.unquote(self.path))
110 f.write("<title>Directory listing for %s</title>\n" % displaypath)
111 f.write("<h2>Directory listing for %s</h2>\n" % displaypath)
112 f.write("<hr>\n<ul>\n")
113 for name in list:
114 fullname = os.path.join(path, name)
115 displayname = linkname = name
116 # Append / for directories or @ for symbolic links
117 if os.path.isdir(fullname):
118 displayname = name + "/"
119 linkname = name + "/"
120 if os.path.islink(fullname):
121 displayname = name + "@"
122 # Note: a link to a directory displays with @ and links with /
123 f.write('<li><a href="%s">%s</a>\n'
124 % (urllib.quote(linkname), cgi.escape(displayname)))
125 f.write("</ul>\n<hr>\n")
126 length = f.tell()
127 f.seek(0)
128 self.send_response(200)
129 self.send_header("Content-type", "text/html")
130 self.send_header("Content-Length", str(length))
131 self.end_headers()
132 return f
133
134 def translate_path(self, path):
135 """Translate a /-separated PATH to the local filename syntax.
136
137 Components that mean special things to the local file system
138 (e.g. drive or directory names) are ignored. (XXX They should
139 probably be diagnosed.)
140
141 """
142 # abandon query parameters
143 path = urlparse.urlparse(path)[2]
144 path = posixpath.normpath(urllib.unquote(path))
145 words = path.split('/')
146 words = filter(None, words)
147 path = os.getcwd()
148 for word in words:
149 drive, word = os.path.splitdrive(word)
150 head, word = os.path.split(word)
151 if word in (os.curdir, os.pardir): continue
152 path = os.path.join(path, word)
153 return path
154
155 def copyfile(self, source, outputfile):
156 """Copy all data between two file objects.
157
158 The SOURCE argument is a file object open for reading
159 (or anything with a read() method) and the DESTINATION
160 argument is a file object open for writing (or
161 anything with a write() method).
162
163 The only reason for overriding this would be to change
164 the block size or perhaps to replace newlines by CRLF
165 -- note however that this the default server uses this
166 to copy binary data as well.
167
168 """
169 shutil.copyfileobj(source, outputfile)
170
171 def guess_type(self, path):
172 """Guess the type of a file.
173
174 Argument is a PATH (a filename).
175
176 Return value is a string of the form type/subtype,
177 usable for a MIME Content-type header.
178
179 The default implementation looks the file's extension
180 up in the table self.extensions_map, using application/octet-stream
181 as a default; however it would be permissible (if
182 slow) to look inside the data to make a better guess.
183
184 """
185
186 base, ext = posixpath.splitext(path)
187 if ext in self.extensions_map:
188 return self.extensions_map[ext]
189 ext = ext.lower()
190 if ext in self.extensions_map:
191 return self.extensions_map[ext]
192 else:
193 return self.extensions_map['']
194
195 if not mimetypes.inited:
196 mimetypes.init() # try to read system mime.types
197 extensions_map = mimetypes.types_map.copy()
198 extensions_map.update({
199 '': 'application/octet-stream', # Default
200 '.py': 'text/plain',
201 '.c': 'text/plain',
202 '.h': 'text/plain',
203 })
204
205
206def test(HandlerClass = SimpleHTTPRequestHandler,
207 ServerClass = BaseHTTPServer.HTTPServer):
208 BaseHTTPServer.test(HandlerClass, ServerClass)
209
210
211if __name__ == '__main__':
212 test()
Note: See TracBrowser for help on using the repository browser.