3 This module builds on BaseHTTPServer by implementing the standard GET
4 and HEAD requests in a fairly straightforward manner.
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
31 The GET and HEAD requests are identical except that the HEAD
32 request omits the actual contents of the file.
36 server_version
= "SimpleHTTP/" + __version__
39 """Serve a GET request."""
42 self
.copyfile(f
, self
.wfile
)
46 """Serve a HEAD request."""
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.
62 path
= self
.translate_path(self
.path
)
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
):
71 return self
.list_directory(path
)
72 ctype
= self
.guess_type(path
)
73 if ctype
.startswith('text/'):
80 self
.send_error(404, "File not found")
82 self
.send_response(200)
83 self
.send_header("Content-type", ctype
)
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().
96 list = os
.listdir(path
)
98 self
.send_error(404, "No permission to list directory");
100 list.sort(lambda a
, b
: cmp(a
.lower(), b
.lower()))
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")
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")
118 self
.send_response(200)
119 self
.send_header("Content-type", "text/html")
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
)
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
)
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
]
180 return self
.extensions_map
['']
183 '': 'text/plain', # Default, *must* be present
184 '.html': 'text/html',
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__':