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
)
85 self
.send_header("Content-Length", str(os
.fstat(f
.fileno())[6]))
89 def list_directory(self
, path
):
90 """Helper to produce a directory listing (absent index.html).
92 Return value is either a file object, or None (indicating an
93 error). In either case, the headers are sent, making the
94 interface the same as for send_head().
98 list = os
.listdir(path
)
100 self
.send_error(404, "No permission to list directory")
102 list.sort(lambda a
, b
: cmp(a
.lower(), b
.lower()))
104 f
.write("<title>Directory listing for %s</title>\n" % self
.path
)
105 f
.write("<h2>Directory listing for %s</h2>\n" % self
.path
)
106 f
.write("<hr>\n<ul>\n")
108 fullname
= os
.path
.join(path
, name
)
109 displayname
= linkname
= name
= cgi
.escape(name
)
110 # Append / for directories or @ for symbolic links
111 if os
.path
.isdir(fullname
):
112 displayname
= name
+ "/"
113 linkname
= name
+ "/"
114 if os
.path
.islink(fullname
):
115 displayname
= name
+ "@"
116 # Note: a link to a directory displays with @ and links with /
117 f
.write('<li><a href="%s">%s</a>\n' % (linkname
, displayname
))
118 f
.write("</ul>\n<hr>\n")
121 self
.send_response(200)
122 self
.send_header("Content-type", "text/html")
123 self
.send_header("Content-Length", str(length
))
127 def translate_path(self
, path
):
128 """Translate a /-separated PATH to the local filename syntax.
130 Components that mean special things to the local file system
131 (e.g. drive or directory names) are ignored. (XXX They should
132 probably be diagnosed.)
135 path
= posixpath
.normpath(urllib
.unquote(path
))
136 words
= path
.split('/')
137 words
= filter(None, words
)
140 drive
, word
= os
.path
.splitdrive(word
)
141 head
, word
= os
.path
.split(word
)
142 if word
in (os
.curdir
, os
.pardir
): continue
143 path
= os
.path
.join(path
, word
)
146 def copyfile(self
, source
, outputfile
):
147 """Copy all data between two file objects.
149 The SOURCE argument is a file object open for reading
150 (or anything with a read() method) and the DESTINATION
151 argument is a file object open for writing (or
152 anything with a write() method).
154 The only reason for overriding this would be to change
155 the block size or perhaps to replace newlines by CRLF
156 -- note however that this the default server uses this
157 to copy binary data as well.
160 shutil
.copyfileobj(source
, outputfile
)
162 def guess_type(self
, path
):
163 """Guess the type of a file.
165 Argument is a PATH (a filename).
167 Return value is a string of the form type/subtype,
168 usable for a MIME Content-type header.
170 The default implementation looks the file's extension
171 up in the table self.extensions_map, using text/plain
172 as a default; however it would be permissible (if
173 slow) to look inside the data to make a better guess.
177 base
, ext
= posixpath
.splitext(path
)
178 if ext
in self
.extensions_map
:
179 return self
.extensions_map
[ext
]
181 if ext
in self
.extensions_map
:
182 return self
.extensions_map
[ext
]
184 return self
.extensions_map
['']
186 extensions_map
= mimetypes
.types_map
.copy()
187 extensions_map
.update({
188 '': 'application/octet-stream', # Default
195 def test(HandlerClass
= SimpleHTTPRequestHandler
,
196 ServerClass
= BaseHTTPServer
.HTTPServer
):
197 BaseHTTPServer
.test(HandlerClass
, ServerClass
)
200 if __name__
== '__main__':