We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
set charset in shareddatamiddleware
# Description
UTF-8 Javascript files served by the SharedDataMiddleware do not contain a charset in the `Content-Type` response header. This leads to issues of javascript interpreted as US-ASCII by browsers (https://github.com/posativ/isso/issues/607).

**Current**
```
Content-Type: application/javascript
```
**Expected**
```
Content-Type: application/javascript; charset=UTF-8
```

The mime type is deducted by using python's mimetypes module and the filename:

https://github.com/pallets/werkzeug/blob/aa9676f5cd5ab7ee08b09f38df22ef3e3c564d87/src/werkzeug/middleware/shared_data.py#L256-L257

It is sent without appending an encoding:

https://github.com/pallets/werkzeug/blob/aa9676f5cd5ab7ee08b09f38df22ef3e3c564d87/src/werkzeug/middleware/shared_data.py#L281

Other usages seem to suggest that a call to `get_content_type` is necessary to append the charset, if applicable:

https://github.com/pallets/werkzeug/blob/e76aac8294626e24e1075e665cbf9657b88c4301/src/werkzeug/wrappers/common_descriptors.py#L146

# Possible solutions
I am not familiar with the codebase, but would it be possible to call `get_content_type`? I tried to modify it myself, but I don't know where to get the encoding from inside  `SharedDataMiddleware`.

My problem is solved when I hardcode 'utf-8' as charset:
```python
("Content-Type", get_content_type(mime_type, 'utf-8'))
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/werkzeug/middleware/shared_data.py`
Content:
```
1 """
2 Serve Shared Static Files
3 =========================
4 
5 .. autoclass:: SharedDataMiddleware
6     :members: is_allowed
7 
8 :copyright: 2007 Pallets
9 :license: BSD-3-Clause
10 """
11 import mimetypes
12 import os
13 import pkgutil
14 import posixpath
15 from datetime import datetime
16 from io import BytesIO
17 from time import mktime
18 from time import time
19 from zlib import adler32
20 
21 from .._compat import PY2
22 from .._compat import string_types
23 from ..filesystem import get_filesystem_encoding
24 from ..http import http_date
25 from ..http import is_resource_modified
26 from ..security import safe_join
27 from ..wsgi import get_path_info
28 from ..wsgi import wrap_file
29 
30 
31 class SharedDataMiddleware(object):
32 
33     """A WSGI middleware that provides static content for development
34     environments or simple server setups. Usage is quite simple::
35 
36         import os
37         from werkzeug.wsgi import SharedDataMiddleware
38 
39         app = SharedDataMiddleware(app, {
40             '/static': os.path.join(os.path.dirname(__file__), 'static')
41         })
42 
43     The contents of the folder ``./shared`` will now be available on
44     ``http://example.com/shared/``.  This is pretty useful during development
45     because a standalone media server is not required.  One can also mount
46     files on the root folder and still continue to use the application because
47     the shared data middleware forwards all unhandled requests to the
48     application, even if the requests are below one of the shared folders.
49 
50     If `pkg_resources` is available you can also tell the middleware to serve
51     files from package data::
52 
53         app = SharedDataMiddleware(app, {
54             '/static': ('myapplication', 'static')
55         })
56 
57     This will then serve the ``static`` folder in the `myapplication`
58     Python package.
59 
60     The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`
61     rules for files that are not accessible from the web.  If `cache` is set to
62     `False` no caching headers are sent.
63 
64     Currently the middleware does not support non ASCII filenames.  If the
65     encoding on the file system happens to be the encoding of the URI it may
66     work but this could also be by accident.  We strongly suggest using ASCII
67     only file names for static files.
68 
69     The middleware will guess the mimetype using the Python `mimetype`
70     module.  If it's unable to figure out the charset it will fall back
71     to `fallback_mimetype`.
72 
73     .. versionchanged:: 0.5
74        The cache timeout is configurable now.
75 
76     .. versionadded:: 0.6
77        The `fallback_mimetype` parameter was added.
78 
79     :param app: the application to wrap.  If you don't want to wrap an
80                 application you can pass it :exc:`NotFound`.
81     :param exports: a list or dict of exported files and folders.
82     :param disallow: a list of :func:`~fnmatch.fnmatch` rules.
83     :param fallback_mimetype: the fallback mimetype for unknown files.
84     :param cache: enable or disable caching headers.
85     :param cache_timeout: the cache timeout in seconds for the headers.
86     """
87 
88     def __init__(
89         self,
90         app,
91         exports,
92         disallow=None,
93         cache=True,
94         cache_timeout=60 * 60 * 12,
95         fallback_mimetype="text/plain",
96     ):
97         self.app = app
98         self.exports = []
99         self.cache = cache
100         self.cache_timeout = cache_timeout
101 
102         if hasattr(exports, "items"):
103             exports = exports.items()
104 
105         for key, value in exports:
106             if isinstance(value, tuple):
107                 loader = self.get_package_loader(*value)
108             elif isinstance(value, string_types):
109                 if os.path.isfile(value):
110                     loader = self.get_file_loader(value)
111                 else:
112                     loader = self.get_directory_loader(value)
113             else:
114                 raise TypeError("unknown def %r" % value)
115 
116             self.exports.append((key, loader))
117 
118         if disallow is not None:
119             from fnmatch import fnmatch
120 
121             self.is_allowed = lambda x: not fnmatch(x, disallow)
122 
123         self.fallback_mimetype = fallback_mimetype
124 
125     def is_allowed(self, filename):
126         """Subclasses can override this method to disallow the access to
127         certain files.  However by providing `disallow` in the constructor
128         this method is overwritten.
129         """
130         return True
131 
132     def _opener(self, filename):
133         return lambda: (
134             open(filename, "rb"),
135             datetime.utcfromtimestamp(os.path.getmtime(filename)),
136             int(os.path.getsize(filename)),
137         )
138 
139     def get_file_loader(self, filename):
140         return lambda x: (os.path.basename(filename), self._opener(filename))
141 
142     def get_package_loader(self, package, package_path):
143         loadtime = datetime.utcnow()
144         provider = pkgutil.get_loader(package)
145 
146         if hasattr(provider, "get_resource_reader"):
147             # Python 3
148             reader = provider.get_resource_reader(package)
149 
150             def loader(path):
151                 if path is None:
152                     return None, None
153 
154                 path = safe_join(package_path, path)
155                 basename = posixpath.basename(path)
156 
157                 try:
158                     resource = reader.open_resource(path)
159                 except IOError:
160                     return None, None
161 
162                 if isinstance(resource, BytesIO):
163                     return (
164                         basename,
165                         lambda: (resource, loadtime, len(resource.getvalue())),
166                     )
167 
168                 return (
169                     basename,
170                     lambda: (
171                         resource,
172                         datetime.utcfromtimestamp(os.path.getmtime(resource.name)),
173                         os.path.getsize(resource.name),
174                     ),
175                 )
176 
177         else:
178             # Python 2
179             package_filename = provider.get_filename(package)
180             is_filesystem = os.path.exists(package_filename)
181             root = os.path.join(os.path.dirname(package_filename), package_path)
182 
183             def loader(path):
184                 if path is None:
185                     return None, None
186 
187                 path = safe_join(root, path)
188                 basename = posixpath.basename(path)
189 
190                 if is_filesystem:
191                     if not os.path.isfile(path):
192                         return None, None
193 
194                     return basename, self._opener(path)
195 
196                 try:
197                     data = provider.get_data(path)
198                 except IOError:
199                     return None, None
200 
201                 return basename, lambda: (BytesIO(data), loadtime, len(data))
202 
203         return loader
204 
205     def get_directory_loader(self, directory):
206         def loader(path):
207             if path is not None:
208                 path = safe_join(directory, path)
209             else:
210                 path = directory
211 
212             if os.path.isfile(path):
213                 return os.path.basename(path), self._opener(path)
214 
215             return None, None
216 
217         return loader
218 
219     def generate_etag(self, mtime, file_size, real_filename):
220         if not isinstance(real_filename, bytes):
221             real_filename = real_filename.encode(get_filesystem_encoding())
222 
223         return "wzsdm-%d-%s-%s" % (
224             mktime(mtime.timetuple()),
225             file_size,
226             adler32(real_filename) & 0xFFFFFFFF,
227         )
228 
229     def __call__(self, environ, start_response):
230         path = get_path_info(environ)
231 
232         if PY2:
233             path = path.encode(get_filesystem_encoding())
234 
235         file_loader = None
236 
237         for search_path, loader in self.exports:
238             if search_path == path:
239                 real_filename, file_loader = loader(None)
240 
241                 if file_loader is not None:
242                     break
243 
244             if not search_path.endswith("/"):
245                 search_path += "/"
246 
247             if path.startswith(search_path):
248                 real_filename, file_loader = loader(path[len(search_path) :])
249 
250                 if file_loader is not None:
251                     break
252 
253         if file_loader is None or not self.is_allowed(real_filename):
254             return self.app(environ, start_response)
255 
256         guessed_type = mimetypes.guess_type(real_filename)
257         mime_type = guessed_type[0] or self.fallback_mimetype
258         f, mtime, file_size = file_loader()
259 
260         headers = [("Date", http_date())]
261 
262         if self.cache:
263             timeout = self.cache_timeout
264             etag = self.generate_etag(mtime, file_size, real_filename)
265             headers += [
266                 ("Etag", '"%s"' % etag),
267                 ("Cache-Control", "max-age=%d, public" % timeout),
268             ]
269 
270             if not is_resource_modified(environ, etag, last_modified=mtime):
271                 f.close()
272                 start_response("304 Not Modified", headers)
273                 return []
274 
275             headers.append(("Expires", http_date(time() + timeout)))
276         else:
277             headers.append(("Cache-Control", "public"))
278 
279         headers.extend(
280             (
281                 ("Content-Type", mime_type),
282                 ("Content-Length", str(file_size)),
283                 ("Last-Modified", http_date(mtime)),
284             )
285         )
286         start_response("200 OK", headers)
287         return wrap_file(environ, f)
288
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/werkzeug/middleware/shared_data.py b/src/werkzeug/middleware/shared_data.py
--- a/src/werkzeug/middleware/shared_data.py
+++ b/src/werkzeug/middleware/shared_data.py
@@ -24,6 +24,7 @@
 from ..http import http_date
 from ..http import is_resource_modified
 from ..security import safe_join
+from ..utils import get_content_type
 from ..wsgi import get_path_info
 from ..wsgi import wrap_file
 
@@ -70,19 +71,24 @@
     module.  If it's unable to figure out the charset it will fall back
     to `fallback_mimetype`.
 
-    .. versionchanged:: 0.5
-       The cache timeout is configurable now.
-
-    .. versionadded:: 0.6
-       The `fallback_mimetype` parameter was added.
-
     :param app: the application to wrap.  If you don't want to wrap an
                 application you can pass it :exc:`NotFound`.
     :param exports: a list or dict of exported files and folders.
     :param disallow: a list of :func:`~fnmatch.fnmatch` rules.
-    :param fallback_mimetype: the fallback mimetype for unknown files.
     :param cache: enable or disable caching headers.
     :param cache_timeout: the cache timeout in seconds for the headers.
+    :param fallback_mimetype: The fallback mimetype for unknown files.
+
+    .. versionchanged:: 1.0
+        The default ``fallback_mimetype`` is
+        ``application/octet-stream``. If a filename looks like a text
+        mimetype, the ``utf-8`` charset is added to it.
+
+    .. versionadded:: 0.6
+        Added ``fallback_mimetype``.
+
+    .. versionchanged:: 0.5
+        Added ``cache_timeout``.
     """
 
     def __init__(
@@ -92,7 +98,7 @@
         disallow=None,
         cache=True,
         cache_timeout=60 * 60 * 12,
-        fallback_mimetype="text/plain",
+        fallback_mimetype="application/octet-stream",
     ):
         self.app = app
         self.exports = []
@@ -254,7 +260,7 @@
             return self.app(environ, start_response)
 
         guessed_type = mimetypes.guess_type(real_filename)
-        mime_type = guessed_type[0] or self.fallback_mimetype
+        mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8")
         f, mtime, file_size = file_loader()
 
         headers = [("Date", http_date())]

{"golden_diff": "diff --git a/src/werkzeug/middleware/shared_data.py b/src/werkzeug/middleware/shared_data.py\n--- a/src/werkzeug/middleware/shared_data.py\n+++ b/src/werkzeug/middleware/shared_data.py\n@@ -24,6 +24,7 @@\n from ..http import http_date\n from ..http import is_resource_modified\n from ..security import safe_join\n+from ..utils import get_content_type\n from ..wsgi import get_path_info\n from ..wsgi import wrap_file\n \n@@ -70,19 +71,24 @@\n     module.  If it's unable to figure out the charset it will fall back\n     to `fallback_mimetype`.\n \n-    .. versionchanged:: 0.5\n-       The cache timeout is configurable now.\n-\n-    .. versionadded:: 0.6\n-       The `fallback_mimetype` parameter was added.\n-\n     :param app: the application to wrap.  If you don't want to wrap an\n                 application you can pass it :exc:`NotFound`.\n     :param exports: a list or dict of exported files and folders.\n     :param disallow: a list of :func:`~fnmatch.fnmatch` rules.\n-    :param fallback_mimetype: the fallback mimetype for unknown files.\n     :param cache: enable or disable caching headers.\n     :param cache_timeout: the cache timeout in seconds for the headers.\n+    :param fallback_mimetype: The fallback mimetype for unknown files.\n+\n+    .. versionchanged:: 1.0\n+        The default ``fallback_mimetype`` is\n+        ``application/octet-stream``. If a filename looks like a text\n+        mimetype, the ``utf-8`` charset is added to it.\n+\n+    .. versionadded:: 0.6\n+        Added ``fallback_mimetype``.\n+\n+    .. versionchanged:: 0.5\n+        Added ``cache_timeout``.\n     \"\"\"\n \n     def __init__(\n@@ -92,7 +98,7 @@\n         disallow=None,\n         cache=True,\n         cache_timeout=60 * 60 * 12,\n-        fallback_mimetype=\"text/plain\",\n+        fallback_mimetype=\"application/octet-stream\",\n     ):\n         self.app = app\n         self.exports = []\n@@ -254,7 +260,7 @@\n             return self.app(environ, start_response)\n \n         guessed_type = mimetypes.guess_type(real_filename)\n-        mime_type = guessed_type[0] or self.fallback_mimetype\n+        mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, \"utf-8\")\n         f, mtime, file_size = file_loader()\n \n         headers = [(\"Date\", http_date())]\n", "issue": "set charset in shareddatamiddleware\n# Description\r\nUTF-8 Javascript files served by the SharedDataMiddleware do not contain a charset in the `Content-Type` response header. This leads to issues of javascript interpreted as US-ASCII by browsers (https://github.com/posativ/isso/issues/607).\r\n\r\n**Current**\r\n```\r\nContent-Type: application/javascript\r\n```\r\n**Expected**\r\n```\r\nContent-Type: application/javascript; charset=UTF-8\r\n```\r\n\r\nThe mime type is deducted by using python's mimetypes module and the filename:\r\n\r\nhttps://github.com/pallets/werkzeug/blob/aa9676f5cd5ab7ee08b09f38df22ef3e3c564d87/src/werkzeug/middleware/shared_data.py#L256-L257\r\n\r\nIt is sent without appending an encoding:\r\n\r\nhttps://github.com/pallets/werkzeug/blob/aa9676f5cd5ab7ee08b09f38df22ef3e3c564d87/src/werkzeug/middleware/shared_data.py#L281\r\n\r\nOther usages seem to suggest that a call to `get_content_type` is necessary to append the charset, if applicable:\r\n\r\nhttps://github.com/pallets/werkzeug/blob/e76aac8294626e24e1075e665cbf9657b88c4301/src/werkzeug/wrappers/common_descriptors.py#L146\r\n\r\n# Possible solutions\r\nI am not familiar with the codebase, but would it be possible to call `get_content_type`? I tried to modify it myself, but I don't know where to get the encoding from inside  `SharedDataMiddleware`.\r\n\r\nMy problem is solved when I hardcode 'utf-8' as charset:\r\n```python\r\n(\"Content-Type\", get_content_type(mime_type, 'utf-8'))\r\n```\n", "before_files": [{"content": "\"\"\"\nServe Shared Static Files\n=========================\n\n.. autoclass:: SharedDataMiddleware\n    :members: is_allowed\n\n:copyright: 2007 Pallets\n:license: BSD-3-Clause\n\"\"\"\nimport mimetypes\nimport os\nimport pkgutil\nimport posixpath\nfrom datetime import datetime\nfrom io import BytesIO\nfrom time import mktime\nfrom time import time\nfrom zlib import adler32\n\nfrom .._compat import PY2\nfrom .._compat import string_types\nfrom ..filesystem import get_filesystem_encoding\nfrom ..http import http_date\nfrom ..http import is_resource_modified\nfrom ..security import safe_join\nfrom ..wsgi import get_path_info\nfrom ..wsgi import wrap_file\n\n\nclass SharedDataMiddleware(object):\n\n    \"\"\"A WSGI middleware that provides static content for development\n    environments or simple server setups. Usage is quite simple::\n\n        import os\n        from werkzeug.wsgi import SharedDataMiddleware\n\n        app = SharedDataMiddleware(app, {\n            '/static': os.path.join(os.path.dirname(__file__), 'static')\n        })\n\n    The contents of the folder ``./shared`` will now be available on\n    ``http://example.com/shared/``.  This is pretty useful during development\n    because a standalone media server is not required.  One can also mount\n    files on the root folder and still continue to use the application because\n    the shared data middleware forwards all unhandled requests to the\n    application, even if the requests are below one of the shared folders.\n\n    If `pkg_resources` is available you can also tell the middleware to serve\n    files from package data::\n\n        app = SharedDataMiddleware(app, {\n            '/static': ('myapplication', 'static')\n        })\n\n    This will then serve the ``static`` folder in the `myapplication`\n    Python package.\n\n    The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`\n    rules for files that are not accessible from the web.  If `cache` is set to\n    `False` no caching headers are sent.\n\n    Currently the middleware does not support non ASCII filenames.  If the\n    encoding on the file system happens to be the encoding of the URI it may\n    work but this could also be by accident.  We strongly suggest using ASCII\n    only file names for static files.\n\n    The middleware will guess the mimetype using the Python `mimetype`\n    module.  If it's unable to figure out the charset it will fall back\n    to `fallback_mimetype`.\n\n    .. versionchanged:: 0.5\n       The cache timeout is configurable now.\n\n    .. versionadded:: 0.6\n       The `fallback_mimetype` parameter was added.\n\n    :param app: the application to wrap.  If you don't want to wrap an\n                application you can pass it :exc:`NotFound`.\n    :param exports: a list or dict of exported files and folders.\n    :param disallow: a list of :func:`~fnmatch.fnmatch` rules.\n    :param fallback_mimetype: the fallback mimetype for unknown files.\n    :param cache: enable or disable caching headers.\n    :param cache_timeout: the cache timeout in seconds for the headers.\n    \"\"\"\n\n    def __init__(\n        self,\n        app,\n        exports,\n        disallow=None,\n        cache=True,\n        cache_timeout=60 * 60 * 12,\n        fallback_mimetype=\"text/plain\",\n    ):\n        self.app = app\n        self.exports = []\n        self.cache = cache\n        self.cache_timeout = cache_timeout\n\n        if hasattr(exports, \"items\"):\n            exports = exports.items()\n\n        for key, value in exports:\n            if isinstance(value, tuple):\n                loader = self.get_package_loader(*value)\n            elif isinstance(value, string_types):\n                if os.path.isfile(value):\n                    loader = self.get_file_loader(value)\n                else:\n                    loader = self.get_directory_loader(value)\n            else:\n                raise TypeError(\"unknown def %r\" % value)\n\n            self.exports.append((key, loader))\n\n        if disallow is not None:\n            from fnmatch import fnmatch\n\n            self.is_allowed = lambda x: not fnmatch(x, disallow)\n\n        self.fallback_mimetype = fallback_mimetype\n\n    def is_allowed(self, filename):\n        \"\"\"Subclasses can override this method to disallow the access to\n        certain files.  However by providing `disallow` in the constructor\n        this method is overwritten.\n        \"\"\"\n        return True\n\n    def _opener(self, filename):\n        return lambda: (\n            open(filename, \"rb\"),\n            datetime.utcfromtimestamp(os.path.getmtime(filename)),\n            int(os.path.getsize(filename)),\n        )\n\n    def get_file_loader(self, filename):\n        return lambda x: (os.path.basename(filename), self._opener(filename))\n\n    def get_package_loader(self, package, package_path):\n        loadtime = datetime.utcnow()\n        provider = pkgutil.get_loader(package)\n\n        if hasattr(provider, \"get_resource_reader\"):\n            # Python 3\n            reader = provider.get_resource_reader(package)\n\n            def loader(path):\n                if path is None:\n                    return None, None\n\n                path = safe_join(package_path, path)\n                basename = posixpath.basename(path)\n\n                try:\n                    resource = reader.open_resource(path)\n                except IOError:\n                    return None, None\n\n                if isinstance(resource, BytesIO):\n                    return (\n                        basename,\n                        lambda: (resource, loadtime, len(resource.getvalue())),\n                    )\n\n                return (\n                    basename,\n                    lambda: (\n                        resource,\n                        datetime.utcfromtimestamp(os.path.getmtime(resource.name)),\n                        os.path.getsize(resource.name),\n                    ),\n                )\n\n        else:\n            # Python 2\n            package_filename = provider.get_filename(package)\n            is_filesystem = os.path.exists(package_filename)\n            root = os.path.join(os.path.dirname(package_filename), package_path)\n\n            def loader(path):\n                if path is None:\n                    return None, None\n\n                path = safe_join(root, path)\n                basename = posixpath.basename(path)\n\n                if is_filesystem:\n                    if not os.path.isfile(path):\n                        return None, None\n\n                    return basename, self._opener(path)\n\n                try:\n                    data = provider.get_data(path)\n                except IOError:\n                    return None, None\n\n                return basename, lambda: (BytesIO(data), loadtime, len(data))\n\n        return loader\n\n    def get_directory_loader(self, directory):\n        def loader(path):\n            if path is not None:\n                path = safe_join(directory, path)\n            else:\n                path = directory\n\n            if os.path.isfile(path):\n                return os.path.basename(path), self._opener(path)\n\n            return None, None\n\n        return loader\n\n    def generate_etag(self, mtime, file_size, real_filename):\n        if not isinstance(real_filename, bytes):\n            real_filename = real_filename.encode(get_filesystem_encoding())\n\n        return \"wzsdm-%d-%s-%s\" % (\n            mktime(mtime.timetuple()),\n            file_size,\n            adler32(real_filename) & 0xFFFFFFFF,\n        )\n\n    def __call__(self, environ, start_response):\n        path = get_path_info(environ)\n\n        if PY2:\n            path = path.encode(get_filesystem_encoding())\n\n        file_loader = None\n\n        for search_path, loader in self.exports:\n            if search_path == path:\n                real_filename, file_loader = loader(None)\n\n                if file_loader is not None:\n                    break\n\n            if not search_path.endswith(\"/\"):\n                search_path += \"/\"\n\n            if path.startswith(search_path):\n                real_filename, file_loader = loader(path[len(search_path) :])\n\n                if file_loader is not None:\n                    break\n\n        if file_loader is None or not self.is_allowed(real_filename):\n            return self.app(environ, start_response)\n\n        guessed_type = mimetypes.guess_type(real_filename)\n        mime_type = guessed_type[0] or self.fallback_mimetype\n        f, mtime, file_size = file_loader()\n\n        headers = [(\"Date\", http_date())]\n\n        if self.cache:\n            timeout = self.cache_timeout\n            etag = self.generate_etag(mtime, file_size, real_filename)\n            headers += [\n                (\"Etag\", '\"%s\"' % etag),\n                (\"Cache-Control\", \"max-age=%d, public\" % timeout),\n            ]\n\n            if not is_resource_modified(environ, etag, last_modified=mtime):\n                f.close()\n                start_response(\"304 Not Modified\", headers)\n                return []\n\n            headers.append((\"Expires\", http_date(time() + timeout)))\n        else:\n            headers.append((\"Cache-Control\", \"public\"))\n\n        headers.extend(\n            (\n                (\"Content-Type\", mime_type),\n                (\"Content-Length\", str(file_size)),\n                (\"Last-Modified\", http_date(mtime)),\n            )\n        )\n        start_response(\"200 OK\", headers)\n        return wrap_file(environ, f)\n", "path": "src/werkzeug/middleware/shared_data.py"}], "after_files": [{"content": "\"\"\"\nServe Shared Static Files\n=========================\n\n.. autoclass:: SharedDataMiddleware\n    :members: is_allowed\n\n:copyright: 2007 Pallets\n:license: BSD-3-Clause\n\"\"\"\nimport mimetypes\nimport os\nimport pkgutil\nimport posixpath\nfrom datetime import datetime\nfrom io import BytesIO\nfrom time import mktime\nfrom time import time\nfrom zlib import adler32\n\nfrom .._compat import PY2\nfrom .._compat import string_types\nfrom ..filesystem import get_filesystem_encoding\nfrom ..http import http_date\nfrom ..http import is_resource_modified\nfrom ..security import safe_join\nfrom ..utils import get_content_type\nfrom ..wsgi import get_path_info\nfrom ..wsgi import wrap_file\n\n\nclass SharedDataMiddleware(object):\n\n    \"\"\"A WSGI middleware that provides static content for development\n    environments or simple server setups. Usage is quite simple::\n\n        import os\n        from werkzeug.wsgi import SharedDataMiddleware\n\n        app = SharedDataMiddleware(app, {\n            '/static': os.path.join(os.path.dirname(__file__), 'static')\n        })\n\n    The contents of the folder ``./shared`` will now be available on\n    ``http://example.com/shared/``.  This is pretty useful during development\n    because a standalone media server is not required.  One can also mount\n    files on the root folder and still continue to use the application because\n    the shared data middleware forwards all unhandled requests to the\n    application, even if the requests are below one of the shared folders.\n\n    If `pkg_resources` is available you can also tell the middleware to serve\n    files from package data::\n\n        app = SharedDataMiddleware(app, {\n            '/static': ('myapplication', 'static')\n        })\n\n    This will then serve the ``static`` folder in the `myapplication`\n    Python package.\n\n    The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`\n    rules for files that are not accessible from the web.  If `cache` is set to\n    `False` no caching headers are sent.\n\n    Currently the middleware does not support non ASCII filenames.  If the\n    encoding on the file system happens to be the encoding of the URI it may\n    work but this could also be by accident.  We strongly suggest using ASCII\n    only file names for static files.\n\n    The middleware will guess the mimetype using the Python `mimetype`\n    module.  If it's unable to figure out the charset it will fall back\n    to `fallback_mimetype`.\n\n    :param app: the application to wrap.  If you don't want to wrap an\n                application you can pass it :exc:`NotFound`.\n    :param exports: a list or dict of exported files and folders.\n    :param disallow: a list of :func:`~fnmatch.fnmatch` rules.\n    :param cache: enable or disable caching headers.\n    :param cache_timeout: the cache timeout in seconds for the headers.\n    :param fallback_mimetype: The fallback mimetype for unknown files.\n\n    .. versionchanged:: 1.0\n        The default ``fallback_mimetype`` is\n        ``application/octet-stream``. If a filename looks like a text\n        mimetype, the ``utf-8`` charset is added to it.\n\n    .. versionadded:: 0.6\n        Added ``fallback_mimetype``.\n\n    .. versionchanged:: 0.5\n        Added ``cache_timeout``.\n    \"\"\"\n\n    def __init__(\n        self,\n        app,\n        exports,\n        disallow=None,\n        cache=True,\n        cache_timeout=60 * 60 * 12,\n        fallback_mimetype=\"application/octet-stream\",\n    ):\n        self.app = app\n        self.exports = []\n        self.cache = cache\n        self.cache_timeout = cache_timeout\n\n        if hasattr(exports, \"items\"):\n            exports = exports.items()\n\n        for key, value in exports:\n            if isinstance(value, tuple):\n                loader = self.get_package_loader(*value)\n            elif isinstance(value, string_types):\n                if os.path.isfile(value):\n                    loader = self.get_file_loader(value)\n                else:\n                    loader = self.get_directory_loader(value)\n            else:\n                raise TypeError(\"unknown def %r\" % value)\n\n            self.exports.append((key, loader))\n\n        if disallow is not None:\n            from fnmatch import fnmatch\n\n            self.is_allowed = lambda x: not fnmatch(x, disallow)\n\n        self.fallback_mimetype = fallback_mimetype\n\n    def is_allowed(self, filename):\n        \"\"\"Subclasses can override this method to disallow the access to\n        certain files.  However by providing `disallow` in the constructor\n        this method is overwritten.\n        \"\"\"\n        return True\n\n    def _opener(self, filename):\n        return lambda: (\n            open(filename, \"rb\"),\n            datetime.utcfromtimestamp(os.path.getmtime(filename)),\n            int(os.path.getsize(filename)),\n        )\n\n    def get_file_loader(self, filename):\n        return lambda x: (os.path.basename(filename), self._opener(filename))\n\n    def get_package_loader(self, package, package_path):\n        loadtime = datetime.utcnow()\n        provider = pkgutil.get_loader(package)\n\n        if hasattr(provider, \"get_resource_reader\"):\n            # Python 3\n            reader = provider.get_resource_reader(package)\n\n            def loader(path):\n                if path is None:\n                    return None, None\n\n                path = safe_join(package_path, path)\n                basename = posixpath.basename(path)\n\n                try:\n                    resource = reader.open_resource(path)\n                except IOError:\n                    return None, None\n\n                if isinstance(resource, BytesIO):\n                    return (\n                        basename,\n                        lambda: (resource, loadtime, len(resource.getvalue())),\n                    )\n\n                return (\n                    basename,\n                    lambda: (\n                        resource,\n                        datetime.utcfromtimestamp(os.path.getmtime(resource.name)),\n                        os.path.getsize(resource.name),\n                    ),\n                )\n\n        else:\n            # Python 2\n            package_filename = provider.get_filename(package)\n            is_filesystem = os.path.exists(package_filename)\n            root = os.path.join(os.path.dirname(package_filename), package_path)\n\n            def loader(path):\n                if path is None:\n                    return None, None\n\n                path = safe_join(root, path)\n                basename = posixpath.basename(path)\n\n                if is_filesystem:\n                    if not os.path.isfile(path):\n                        return None, None\n\n                    return basename, self._opener(path)\n\n                try:\n                    data = provider.get_data(path)\n                except IOError:\n                    return None, None\n\n                return basename, lambda: (BytesIO(data), loadtime, len(data))\n\n        return loader\n\n    def get_directory_loader(self, directory):\n        def loader(path):\n            if path is not None:\n                path = safe_join(directory, path)\n            else:\n                path = directory\n\n            if os.path.isfile(path):\n                return os.path.basename(path), self._opener(path)\n\n            return None, None\n\n        return loader\n\n    def generate_etag(self, mtime, file_size, real_filename):\n        if not isinstance(real_filename, bytes):\n            real_filename = real_filename.encode(get_filesystem_encoding())\n\n        return \"wzsdm-%d-%s-%s\" % (\n            mktime(mtime.timetuple()),\n            file_size,\n            adler32(real_filename) & 0xFFFFFFFF,\n        )\n\n    def __call__(self, environ, start_response):\n        path = get_path_info(environ)\n\n        if PY2:\n            path = path.encode(get_filesystem_encoding())\n\n        file_loader = None\n\n        for search_path, loader in self.exports:\n            if search_path == path:\n                real_filename, file_loader = loader(None)\n\n                if file_loader is not None:\n                    break\n\n            if not search_path.endswith(\"/\"):\n                search_path += \"/\"\n\n            if path.startswith(search_path):\n                real_filename, file_loader = loader(path[len(search_path) :])\n\n                if file_loader is not None:\n                    break\n\n        if file_loader is None or not self.is_allowed(real_filename):\n            return self.app(environ, start_response)\n\n        guessed_type = mimetypes.guess_type(real_filename)\n        mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, \"utf-8\")\n        f, mtime, file_size = file_loader()\n\n        headers = [(\"Date\", http_date())]\n\n        if self.cache:\n            timeout = self.cache_timeout\n            etag = self.generate_etag(mtime, file_size, real_filename)\n            headers += [\n                (\"Etag\", '\"%s\"' % etag),\n                (\"Cache-Control\", \"max-age=%d, public\" % timeout),\n            ]\n\n            if not is_resource_modified(environ, etag, last_modified=mtime):\n                f.close()\n                start_response(\"304 Not Modified\", headers)\n                return []\n\n            headers.append((\"Expires\", http_date(time() + timeout)))\n        else:\n            headers.append((\"Cache-Control\", \"public\"))\n\n        headers.extend(\n            (\n                (\"Content-Type\", mime_type),\n                (\"Content-Length\", str(file_size)),\n                (\"Last-Modified\", http_date(mtime)),\n            )\n        )\n        start_response(\"200 OK\", headers)\n        return wrap_file(environ, f)\n", "path": "src/werkzeug/middleware/shared_data.py"}]}