|
1 | 1 | import json
|
2 |
| -import time |
3 | 2 | import os
|
| 3 | +import time |
| 4 | +from functools import lru_cache |
4 | 5 | from io import open
|
| 6 | +from typing import Dict, Optional |
| 7 | +from urllib.parse import urlparse |
| 8 | +from warnings import warn |
5 | 9 |
|
6 | 10 | from django.conf import settings
|
7 | 11 | from django.contrib.staticfiles.storage import staticfiles_storage
|
| 12 | +from django.http import HttpRequest |
8 | 13 |
|
9 | 14 | from .exceptions import (
|
10 | 15 | WebpackError,
|
|
13 | 18 | WebpackBundleLookupError,
|
14 | 19 | )
|
15 | 20 |
|
| 21 | +_CROSSORIGIN_NO_REQUEST = ( |
| 22 | + 'The crossorigin attribute might be necessary but you did not pass a ' |
| 23 | + 'request object. django_webpack_loader needs a request object to be able ' |
| 24 | + 'to know when to emit the crossorigin attribute on link and script tags.') |
| 25 | +_CROSSORIGIN_NO_HOST = ( |
| 26 | + 'You have passed the request object but it does not have a "HTTP_HOST", ' |
| 27 | + 'thus django_webpack_loader can\'t know if the crossorigin header will ' |
| 28 | + 'be necessary or not.') |
| 29 | + |
| 30 | + |
| 31 | +@lru_cache(maxsize=100) |
| 32 | +def _get_netloc(url: str) -> str: |
| 33 | + 'Return a cached netloc (host:port) for the passed `url`.' |
| 34 | + return urlparse(url=url).netloc |
| 35 | + |
16 | 36 |
|
17 | 37 | class WebpackLoader:
|
18 | 38 | _assets = {}
|
@@ -42,19 +62,46 @@ def get_asset_by_source_filename(self, name):
|
42 | 62 | files = self.get_assets()["assets"].values()
|
43 | 63 | return next((x for x in files if x.get("sourceFilename") == name), None)
|
44 | 64 |
|
45 |
| - def get_integrity_attr(self, chunk): |
46 |
| - if not self.config.get("INTEGRITY"): |
47 |
| - return " " |
48 |
| - |
49 |
| - integrity = chunk.get("integrity") |
| 65 | + def _add_crossorigin( |
| 66 | + self, request: Optional[HttpRequest], chunk_url: str, |
| 67 | + integrity: str, attrs: str) -> str: |
| 68 | + 'Return an added `crossorigin` attribute if necessary.' |
| 69 | + def_value = f' integrity="{integrity}" ' |
| 70 | + cfgval: str = self.config.get('CROSSORIGIN') |
| 71 | + if not request: |
| 72 | + warn(message=_CROSSORIGIN_NO_REQUEST, category=RuntimeWarning) |
| 73 | + return def_value |
| 74 | + if 'crossorigin' in attrs.lower(): |
| 75 | + return def_value |
| 76 | + host: Optional[str] = request.META.get('HTTP_HOST') |
| 77 | + if not host: |
| 78 | + warn(message=_CROSSORIGIN_NO_HOST, category=RuntimeWarning) |
| 79 | + return def_value |
| 80 | + netloc = _get_netloc(url=chunk_url) |
| 81 | + if netloc == '' or netloc == host: |
| 82 | + # Crossorigin not necessary |
| 83 | + return def_value |
| 84 | + if cfgval == '': |
| 85 | + return f'{def_value}crossorigin ' |
| 86 | + return f'{def_value}crossorigin="{cfgval}" ' |
| 87 | + |
| 88 | + def get_integrity_attr( |
| 89 | + self, chunk: Dict[str, str], request: Optional[HttpRequest], |
| 90 | + attrs: str): |
| 91 | + if not self.config.get('INTEGRITY'): |
| 92 | + # Crossorigin only necessary when integrity is used |
| 93 | + return ' ' |
| 94 | + |
| 95 | + integrity = chunk.get('integrity') |
50 | 96 | if not integrity:
|
51 | 97 | raise WebpackLoaderBadStatsError(
|
52 |
| - "The stats file does not contain valid data: INTEGRITY is set to True, " |
53 |
| - 'but chunk does not contain "integrity" key. Maybe you forgot to add ' |
54 |
| - "integrity: true in your BundleTracker configuration?" |
55 |
| - ) |
56 |
| - |
57 |
| - return ' integrity="{}" '.format(integrity.partition(" ")[0]) |
| 98 | + 'The stats file does not contain valid data: INTEGRITY is set ' |
| 99 | + 'to True, but chunk does not contain "integrity" key. Maybe ' |
| 100 | + 'you forgot to add integrity: true in your ' |
| 101 | + 'BundleTrackerPlugin configuration?') |
| 102 | + return self._add_crossorigin( |
| 103 | + request=request, chunk_url=chunk['url'], integrity=integrity, |
| 104 | + attrs=attrs) |
58 | 105 |
|
59 | 106 | def filter_chunks(self, chunks):
|
60 | 107 | filtered_chunks = []
|
|
0 commit comments