diff -r 992ce0dcfb29 Lib/http/server.py --- a/Lib/http/server.py Thu Jan 15 22:52:59 2015 +0100 +++ b/Lib/http/server.py Sat Jan 17 19:20:25 2015 +0530 @@ -672,6 +672,8 @@ server_version = "SimpleHTTP/" + __version__ + base_files = ['index.html', 'index.html'] + def do_GET(self): """Serve a GET request.""" f = self.send_head() @@ -702,40 +704,70 @@ f = None if os.path.isdir(path): parts = urllib.parse.urlsplit(self.path) - if not parts.path.endswith('/'): - # redirect browser - doing basically what apache does - self.send_response(301) - new_parts = (parts[0], parts[1], parts[2] + '/', - parts[3], parts[4]) - new_url = urllib.parse.urlunsplit(new_parts) - self.send_header("Location", new_url) - self.end_headers() + + if self.redirect_browser(path, parts): return None - for index in "index.html", "index.htm": - index = os.path.join(path, index) - if os.path.exists(index): - path = index - break - else: - return self.list_directory(path) - ctype = self.guess_type(path) + + path = self.get_html_or_dir_path(path) + if isinstance(path, io.BytesIO): + return path + + f = self.get_file(path) + if not f: + return None + try: - f = open(path, 'rb') - except OSError: - self.send_error(404, "File not found") - return None - try: - self.send_response(200) - self.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) - self.send_header("Content-Length", str(fs[6])) - self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) - self.end_headers() + self.apply_headers(f, path) return f except: f.close() raise + def redirect_browser(self, path, parts): + """If the path does not have a trailing backslash, + redirect browser to path with trailing backslash. + Similar to Apache. + """ + if not parts.path.endswith('/'): + self.send_response(301) + new_parts = (parts[0], parts[1], parts[2] + "/", + parts[3], parts[4]) + new_url = urllib.parse.urlunsplit(new_parts) + self.send_header("Location", new_url) + self.end_headers() + return True + return False + + def get_html_or_dir_path(self, path): + """For the given `path`, depending on if it is file or directory + return file object for a file and io.BytesIO object for a directory. + """ + for base_file in self.base_files: + index = os.path.join(path, base_file) + if os.path.exists(index): + return index + else: + return self.list_directory(path) + + def get_file(self, path): + """Open path or send 404""" + try: + return open(path, 'rb') + except OSError: + self.send_error(404, "File not found") + return None + + def apply_headers(self, f, path): + """Apply remaining headers before sending back the file object""" + ctype = self.guess_type(path) + + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + def list_directory(self, path): """Helper to produce a directory listing (absent index.html).