def list_directory( self, path ): # Patches the list_directory method so that files an be served but directories not listed. if config.getboolean('DEFAULT', 'servefiles') and config.getboolean( 'DEFAULT', 'listdirs'): SimpleHTTPRequestHandler.list_directory( self, path ) # If listing is allowed, then do what the original method does else: self.send_error(403) #No permission to list directory return None # Effectively, all directory listing is blocked
def list_directory(self, path): if self.file_only: self.send_error(404, "No such file") else: return SimpleHTTPRequestHandler.list_directory(self, path)
def send_head(self): path = self.path.split("?",1)[0].split("#",1)[0] #strip path after second slash: while path.rfind("/", 1)>0: path = path[:path.rfind("/", 1)] script = self.script_paths.get(path) log("send_head() script(%s)=%s", path, script) if script: log("request for %s handled using %s", path, script) content = script(self) return content path = self.translate_path(self.path) if not path or not os.path.exists(path): self.send_error(404, "Path not found") return None if os.path.isdir(path): if not path.endswith('/'): # redirect browser - doing basically what apache does self.send_response(301) self.send_header("Location", path + "/") self.end_headers() return None for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: if not self.directory_listing: self.send_error(403, "Directory listing forbidden") return None return SimpleHTTPRequestHandler.list_directory(self, path).read() ext = os.path.splitext(path)[1] f = None try: # Always read in binary mode. Opening files in text mode may cause # newline translations, making the actual size of the content # transmitted *less* than the content-length! f = open(path, 'rb') fs = os.fstat(f.fileno()) content_length = fs[6] content_type = EXTENSION_TO_MIMETYPE.get(ext) if not content_type: if not mimetypes.inited: mimetypes.init() ctype = mimetypes.guess_type(path, False) if ctype and ctype[0]: content_type = ctype[0] log("guess_type(%s)=%s", path, content_type) if content_type: self.extra_headers["Content-type"] = content_type accept = self.headers.get('accept-encoding', '').split(",") accept = tuple(x.split(";")[0].strip() for x in accept) content = None log("accept-encoding=%s", csv(accept)) for enc in HTTP_ACCEPT_ENCODING: #find a matching pre-compressed file: if enc not in accept: continue compressed_path = "%s.%s" % (path, enc) #ie: "/path/to/index.html.br" if not os.path.exists(compressed_path): continue if not os.path.isfile(compressed_path): log.warn("Warning: '%s' is not a file!", compressed_path) continue if not os.access(compressed_path, os.R_OK): log.warn("Warning: '%s' is not readable", compressed_path) continue st = os.stat(compressed_path) if st.st_size==0: log.warn("Warning: '%s' is empty", compressed_path) continue log("sending pre-compressed file '%s'", compressed_path) #read pre-gzipped file: f.close() f = None f = open(compressed_path, 'rb') content = f.read() assert content, "no data in %s" % compressed_path self.extra_headers["Content-Encoding"] = enc break if not content: content = f.read() assert len(content)==content_length, \ "expected %s to contain %i bytes but read %i bytes" % (path, content_length, len(content)) if content_length>128 and \ ("gzip" in accept) and \ ("gzip" in HTTP_ACCEPT_ENCODING) \ and (ext not in (".png", )): #gzip it on the fly: import zlib assert len(content)==content_length, \ "expected %s to contain %i bytes but read %i bytes" % (path, content_length, len(content)) gzip_compress = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16) compressed_content = gzip_compress.compress(content) + gzip_compress.flush() if len(compressed_content)<content_length: log("gzip compressed '%s': %i down to %i bytes", path, content_length, len(compressed_content)) self.extra_headers["Content-Encoding"] = "gzip" content = compressed_content f.close() f = None #send back response headers: self.send_response(200) self.extra_headers.update({ "Content-Length" : len(content), "Last-Modified" : self.date_time_string(fs.st_mtime), }) self.end_headers() except IOError as e: self.close_connection = True log("send_head()", exc_info=True) log.error("Error sending '%s':", path) emsg = str(e) if emsg.endswith(": '%s'" % path): log.error(" %s", emsg.rsplit(":", 1)[0]) else: log.error(" %s", e) try: self.send_error(404, "File not found") except OSError: log("failed to send 404 error - maybe some of the headers were already sent?", exc_info=True) return None finally: if f: try: f.close() except OSError: log("failed to close", exc_info=True) return content