| 262 | self._access_validation = access_validation |
| 263 | |
| 264 | def get(self, path, include_body=True): |
| 265 | if self._access_validation is not None: |
| 266 | self._access_validation(self.request) |
| 267 | |
| 268 | path = self.parse_url_path(path) |
| 269 | abspath = os.path.abspath(os.path.join(self.root, path)) |
| 270 | # os.path.abspath strips a trailing / |
| 271 | # it needs to be temporarily added back for requests to root/ |
| 272 | if not (abspath + os.path.sep).startswith(self.root): |
| 273 | raise HTTPError(403, "%s is not in root static directory", path) |
| 274 | if os.path.isdir(abspath) and self.default_filename is not None: |
| 275 | # need to look at the request.path here for when path is empty |
| 276 | # but there is some prefix to the path that was already |
| 277 | # trimmed by the routing |
| 278 | if not self.request.path.endswith("/"): |
| 279 | self.redirect(self.request.path + "/") |
| 280 | return |
| 281 | abspath = os.path.join(abspath, self.default_filename) |
| 282 | if not os.path.exists(abspath): |
| 283 | raise HTTPError(404) |
| 284 | if not os.path.isfile(abspath): |
| 285 | raise HTTPError(403, "%s is not a file", path) |
| 286 | |
| 287 | stat_result = os.stat(abspath) |
| 288 | modified = datetime.datetime.fromtimestamp(stat_result[stat.ST_MTIME]) |
| 289 | |
| 290 | self.set_header("Last-Modified", modified) |
| 291 | |
| 292 | mime_type, encoding = mimetypes.guess_type(abspath) |
| 293 | if mime_type: |
| 294 | self.set_header("Content-Type", mime_type) |
| 295 | |
| 296 | cache_time = self.get_cache_time(path, modified, mime_type) |
| 297 | |
| 298 | if cache_time > 0: |
| 299 | self.set_header("Expires", datetime.datetime.utcnow() + |
| 300 | datetime.timedelta(seconds=cache_time)) |
| 301 | self.set_header("Cache-Control", "max-age=" + str(cache_time)) |
| 302 | |
| 303 | self.set_extra_headers(path) |
| 304 | |
| 305 | # Check the If-Modified-Since, and don't send the result if the |
| 306 | # content has not been modified |
| 307 | ims_value = self.request.headers.get("If-Modified-Since") |
| 308 | if ims_value is not None: |
| 309 | date_tuple = email.utils.parsedate(ims_value) |
| 310 | if_since = datetime.datetime.fromtimestamp(time.mktime(date_tuple)) |
| 311 | if if_since >= modified: |
| 312 | self.set_status(304) |
| 313 | return |
| 314 | |
| 315 | if not include_body: |
| 316 | assert self.request.method == "HEAD" |
| 317 | self.set_header("Content-Length", stat_result[stat.ST_SIZE]) |
| 318 | else: |
| 319 | with open(abspath, "rb") as file: |
| 320 | while True: |
| 321 | data = file.read(LargeResponseHandler.CHUNK_SIZE) |