3 This module builds on BaseHTTPServer by implementing the standard GET
4 and HEAD requests in a fairly straightforward manner.
11 __all__
= ["SimpleHTTPRequestHandler"]
20 from StringIO
import StringIO
23 class SimpleHTTPRequestHandler(BaseHTTPServer
.BaseHTTPRequestHandler
):
25 """Simple HTTP request handler with GET and HEAD commands.
27 This serves files from the current directory and any of its
28 subdirectories. It assumes that all files are plain text files
29 unless they have the extension ".html" in which case it assumes
32 The GET and HEAD requests are identical except that the HEAD
33 request omits the actual contents of the file.
37 server_version
= "SimpleHTTP/" + __version__
40 """Serve a GET request."""
43 self
.copyfile(f
, self
.wfile
)
47 """Serve a HEAD request."""
53 """Common code for GET and HEAD commands.
55 This sends the response code and MIME headers.
57 Return value is either a file object (which has to be copied
58 to the outputfile by the caller unless the command was HEAD,
59 and must be closed by the caller under all circumstances), or
60 None, in which case the caller has nothing further to do.
63 path
= self
.translate_path(self
.path
)
65 if os
.path
.isdir(path
):
66 for index
in "index.html", "index.htm":
67 index
= os
.path
.join(path
, index
)
68 if os
.path
.exists(index
):
72 return self
.list_directory(path
)
73 ctype
= self
.guess_type(path
)
74 if ctype
.startswith('text/'):
81 self
.send_error(404, "File not found")
83 self
.send_response(200)
84 self
.send_header("Content-type", ctype
)
88 def list_directory(self
, path
):
89 """Helper to produce a directory listing (absent index.html).
91 Return value is either a file object, or None (indicating an
92 error). In either case, the headers are sent, making the
93 interface the same as for send_head().
97 list = os
.listdir(path
)
99 self
.send_error(404, "No permission to list directory")
101 list.sort(lambda a
, b
: cmp(a
.lower(), b
.lower()))
103 f
.write("<title>Directory listing for %s</title>\n" % self
.path
)
104 f
.write("<h2>Directory listing for %s</h2>\n" % self
.path
)
105 f
.write("<hr>\n<ul>\n")
107 fullname
= os
.path
.join(path
, name
)
108 displayname
= linkname
= name
= cgi
.escape(name
)
109 # Append / for directories or @ for symbolic links
110 if os
.path
.isdir(fullname
):
111 displayname
= name
+ "/"
112 linkname
= name
+ "/"
113 if os
.path
.islink(fullname
):
114 displayname
= name
+ "@"
115 # Note: a link to a directory displays with @ and links with /
116 f
.write('<li><a href="%s">%s</a>\n' % (linkname
, displayname
))
117 f
.write("</ul>\n<hr>\n")
119 self
.send_response(200)
120 self
.send_header("Content-type", "text/html")
124 def translate_path(self
, path
):
125 """Translate a /-separated PATH to the local filename syntax.
127 Components that mean special things to the local file system
128 (e.g. drive or directory names) are ignored. (XXX They should
129 probably be diagnosed.)
132 path
= posixpath
.normpath(urllib
.unquote(path
))
133 words
= path
.split('/')
134 words
= filter(None, words
)
137 drive
, word
= os
.path
.splitdrive(word
)
138 head
, word
= os
.path
.split(word
)
139 if word
in (os
.curdir
, os
.pardir
): continue
140 path
= os
.path
.join(path
, word
)
143 def copyfile(self
, source
, outputfile
):
144 """Copy all data between two file objects.
146 The SOURCE argument is a file object open for reading
147 (or anything with a read() method) and the DESTINATION
148 argument is a file object open for writing (or
149 anything with a write() method).
151 The only reason for overriding this would be to change
152 the block size or perhaps to replace newlines by CRLF
153 -- note however that this the default server uses this
154 to copy binary data as well.
157 shutil
.copyfileobj(source
, outputfile
)
159 def guess_type(self
, path
):
160 """Guess the type of a file.
162 Argument is a PATH (a filename).
164 Return value is a string of the form type/subtype,
165 usable for a MIME Content-type header.
167 The default implementation looks the file's extension
168 up in the table self.extensions_map, using text/plain
169 as a default; however it would be permissible (if
170 slow) to look inside the data to make a better guess.
174 base
, ext
= posixpath
.splitext(path
)
175 if self
.extensions_map
.has_key(ext
):
176 return self
.extensions_map
[ext
]
178 if self
.extensions_map
.has_key(ext
):
179 return self
.extensions_map
[ext
]
181 return self
.extensions_map
['']
183 extensions_map
= mimetypes
.types_map
.copy()
184 extensions_map
.update({
185 '': 'application/octet-stream', # Default
192 def test(HandlerClass
= SimpleHTTPRequestHandler
,
193 ServerClass
= BaseHTTPServer
.HTTPServer
):
194 BaseHTTPServer
.test(HandlerClass
, ServerClass
)
197 if __name__
== '__main__':