We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`FileStorage.content_length` raises `TypeError` if no length was provided
I'm trying to get a file size using property `content_length` of class `werkzeug.datastructures.FileStorage`, but getting an error
```
File ".../services/file.py", line 18, in save_web_file
    if file.content_length > (2**8) * (2**10) * 100:  # 100KB
  File ".../lib/python3.10/site-packages/werkzeug/datastructures/file_storage.py", line 71, in content_length
    return _plain_int(self.headers.get("content-length") or 0)
  File ".../lib/python3.10/site-packages/werkzeug/_internal.py", line 326, in _plain_int
    if _plain_int_re.fullmatch(value) is None:
TypeError: expected string or bytes-like object
```
To reproduce this, you need to POST file form without `content_length` 

To fix it, i think we need to change 0 as int to '0', because _plain_int expects value as str, not int, but when we haven't content-length header or have empty field, 0 as int will passed.
```python
@property
def content_length(self):
    """The content-length sent in the header.  Usually not available"""
    try:
        return _plain_int(self.headers.get("content-length") or 0) # here 0 -> '0'
    except ValueError:
        return 0
 ```

Environment:

- Python version: 3.10
- Flask version: 2.3.2


--- 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/datastructures/file_storage.py`
Content:
```
1 from __future__ import annotations
2 
3 import mimetypes
4 from io import BytesIO
5 from os import fsdecode
6 from os import fspath
7 
8 from .._internal import _plain_int
9 from .structures import MultiDict
10 
11 
12 class FileStorage:
13     """The :class:`FileStorage` class is a thin wrapper over incoming files.
14     It is used by the request object to represent uploaded files.  All the
15     attributes of the wrapper stream are proxied by the file storage so
16     it's possible to do ``storage.read()`` instead of the long form
17     ``storage.stream.read()``.
18     """
19 
20     def __init__(
21         self,
22         stream=None,
23         filename=None,
24         name=None,
25         content_type=None,
26         content_length=None,
27         headers=None,
28     ):
29         self.name = name
30         self.stream = stream or BytesIO()
31 
32         # If no filename is provided, attempt to get the filename from
33         # the stream object. Python names special streams like
34         # ``<stderr>`` with angular brackets, skip these streams.
35         if filename is None:
36             filename = getattr(stream, "name", None)
37 
38             if filename is not None:
39                 filename = fsdecode(filename)
40 
41             if filename and filename[0] == "<" and filename[-1] == ">":
42                 filename = None
43         else:
44             filename = fsdecode(filename)
45 
46         self.filename = filename
47 
48         if headers is None:
49             from .headers import Headers
50 
51             headers = Headers()
52         self.headers = headers
53         if content_type is not None:
54             headers["Content-Type"] = content_type
55         if content_length is not None:
56             headers["Content-Length"] = str(content_length)
57 
58     def _parse_content_type(self):
59         if not hasattr(self, "_parsed_content_type"):
60             self._parsed_content_type = http.parse_options_header(self.content_type)
61 
62     @property
63     def content_type(self):
64         """The content-type sent in the header.  Usually not available"""
65         return self.headers.get("content-type")
66 
67     @property
68     def content_length(self):
69         """The content-length sent in the header.  Usually not available"""
70         try:
71             return _plain_int(self.headers.get("content-length") or 0)
72         except ValueError:
73             return 0
74 
75     @property
76     def mimetype(self):
77         """Like :attr:`content_type`, but without parameters (eg, without
78         charset, type etc.) and always lowercase.  For example if the content
79         type is ``text/HTML; charset=utf-8`` the mimetype would be
80         ``'text/html'``.
81 
82         .. versionadded:: 0.7
83         """
84         self._parse_content_type()
85         return self._parsed_content_type[0].lower()
86 
87     @property
88     def mimetype_params(self):
89         """The mimetype parameters as dict.  For example if the content
90         type is ``text/html; charset=utf-8`` the params would be
91         ``{'charset': 'utf-8'}``.
92 
93         .. versionadded:: 0.7
94         """
95         self._parse_content_type()
96         return self._parsed_content_type[1]
97 
98     def save(self, dst, buffer_size=16384):
99         """Save the file to a destination path or file object.  If the
100         destination is a file object you have to close it yourself after the
101         call.  The buffer size is the number of bytes held in memory during
102         the copy process.  It defaults to 16KB.
103 
104         For secure file saving also have a look at :func:`secure_filename`.
105 
106         :param dst: a filename, :class:`os.PathLike`, or open file
107             object to write to.
108         :param buffer_size: Passed as the ``length`` parameter of
109             :func:`shutil.copyfileobj`.
110 
111         .. versionchanged:: 1.0
112             Supports :mod:`pathlib`.
113         """
114         from shutil import copyfileobj
115 
116         close_dst = False
117 
118         if hasattr(dst, "__fspath__"):
119             dst = fspath(dst)
120 
121         if isinstance(dst, str):
122             dst = open(dst, "wb")
123             close_dst = True
124 
125         try:
126             copyfileobj(self.stream, dst, buffer_size)
127         finally:
128             if close_dst:
129                 dst.close()
130 
131     def close(self):
132         """Close the underlying file if possible."""
133         try:
134             self.stream.close()
135         except Exception:
136             pass
137 
138     def __bool__(self):
139         return bool(self.filename)
140 
141     def __getattr__(self, name):
142         try:
143             return getattr(self.stream, name)
144         except AttributeError:
145             # SpooledTemporaryFile doesn't implement IOBase, get the
146             # attribute from its backing file instead.
147             # https://github.com/python/cpython/pull/3249
148             if hasattr(self.stream, "_file"):
149                 return getattr(self.stream._file, name)
150             raise
151 
152     def __iter__(self):
153         return iter(self.stream)
154 
155     def __repr__(self):
156         return f"<{type(self).__name__}: {self.filename!r} ({self.content_type!r})>"
157 
158 
159 class FileMultiDict(MultiDict):
160     """A special :class:`MultiDict` that has convenience methods to add
161     files to it.  This is used for :class:`EnvironBuilder` and generally
162     useful for unittesting.
163 
164     .. versionadded:: 0.5
165     """
166 
167     def add_file(self, name, file, filename=None, content_type=None):
168         """Adds a new file to the dict.  `file` can be a file name or
169         a :class:`file`-like or a :class:`FileStorage` object.
170 
171         :param name: the name of the field.
172         :param file: a filename or :class:`file`-like object
173         :param filename: an optional filename
174         :param content_type: an optional content type
175         """
176         if isinstance(file, FileStorage):
177             value = file
178         else:
179             if isinstance(file, str):
180                 if filename is None:
181                     filename = file
182                 file = open(file, "rb")
183             if filename and content_type is None:
184                 content_type = (
185                     mimetypes.guess_type(filename)[0] or "application/octet-stream"
186                 )
187             value = FileStorage(file, filename, name, content_type)
188 
189         self.add(name, value)
190 
191 
192 # circular dependencies
193 from .. import http
194
```
--- 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/datastructures/file_storage.py b/src/werkzeug/datastructures/file_storage.py
--- a/src/werkzeug/datastructures/file_storage.py
+++ b/src/werkzeug/datastructures/file_storage.py
@@ -67,10 +67,13 @@
     @property
     def content_length(self):
         """The content-length sent in the header.  Usually not available"""
-        try:
-            return _plain_int(self.headers.get("content-length") or 0)
-        except ValueError:
-            return 0
+        if "content-length" in self.headers:
+            try:
+                return _plain_int(self.headers["content-length"])
+            except ValueError:
+                pass
+
+        return 0
 
     @property
     def mimetype(self):

{"golden_diff": "diff --git a/src/werkzeug/datastructures/file_storage.py b/src/werkzeug/datastructures/file_storage.py\n--- a/src/werkzeug/datastructures/file_storage.py\n+++ b/src/werkzeug/datastructures/file_storage.py\n@@ -67,10 +67,13 @@\n     @property\n     def content_length(self):\n         \"\"\"The content-length sent in the header.  Usually not available\"\"\"\n-        try:\n-            return _plain_int(self.headers.get(\"content-length\") or 0)\n-        except ValueError:\n-            return 0\n+        if \"content-length\" in self.headers:\n+            try:\n+                return _plain_int(self.headers[\"content-length\"])\n+            except ValueError:\n+                pass\n+\n+        return 0\n \n     @property\n     def mimetype(self):\n", "issue": "`FileStorage.content_length` raises `TypeError` if no length was provided\nI'm trying to get a file size using property `content_length` of class `werkzeug.datastructures.FileStorage`, but getting an error\r\n```\r\nFile \".../services/file.py\", line 18, in save_web_file\r\n    if file.content_length > (2**8) * (2**10) * 100:  # 100KB\r\n  File \".../lib/python3.10/site-packages/werkzeug/datastructures/file_storage.py\", line 71, in content_length\r\n    return _plain_int(self.headers.get(\"content-length\") or 0)\r\n  File \".../lib/python3.10/site-packages/werkzeug/_internal.py\", line 326, in _plain_int\r\n    if _plain_int_re.fullmatch(value) is None:\r\nTypeError: expected string or bytes-like object\r\n```\r\nTo reproduce this, you need to POST file form without `content_length` \r\n\r\nTo fix it, i think we need to change 0 as int to '0', because _plain_int expects value as str, not int, but when we haven't content-length header or have empty field, 0 as int will passed.\r\n```python\r\n@property\r\ndef content_length(self):\r\n    \"\"\"The content-length sent in the header.  Usually not available\"\"\"\r\n    try:\r\n        return _plain_int(self.headers.get(\"content-length\") or 0) # here 0 -> '0'\r\n    except ValueError:\r\n        return 0\r\n ```\r\n\r\nEnvironment:\r\n\r\n- Python version: 3.10\r\n- Flask version: 2.3.2\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nimport mimetypes\nfrom io import BytesIO\nfrom os import fsdecode\nfrom os import fspath\n\nfrom .._internal import _plain_int\nfrom .structures import MultiDict\n\n\nclass FileStorage:\n    \"\"\"The :class:`FileStorage` class is a thin wrapper over incoming files.\n    It is used by the request object to represent uploaded files.  All the\n    attributes of the wrapper stream are proxied by the file storage so\n    it's possible to do ``storage.read()`` instead of the long form\n    ``storage.stream.read()``.\n    \"\"\"\n\n    def __init__(\n        self,\n        stream=None,\n        filename=None,\n        name=None,\n        content_type=None,\n        content_length=None,\n        headers=None,\n    ):\n        self.name = name\n        self.stream = stream or BytesIO()\n\n        # If no filename is provided, attempt to get the filename from\n        # the stream object. Python names special streams like\n        # ``<stderr>`` with angular brackets, skip these streams.\n        if filename is None:\n            filename = getattr(stream, \"name\", None)\n\n            if filename is not None:\n                filename = fsdecode(filename)\n\n            if filename and filename[0] == \"<\" and filename[-1] == \">\":\n                filename = None\n        else:\n            filename = fsdecode(filename)\n\n        self.filename = filename\n\n        if headers is None:\n            from .headers import Headers\n\n            headers = Headers()\n        self.headers = headers\n        if content_type is not None:\n            headers[\"Content-Type\"] = content_type\n        if content_length is not None:\n            headers[\"Content-Length\"] = str(content_length)\n\n    def _parse_content_type(self):\n        if not hasattr(self, \"_parsed_content_type\"):\n            self._parsed_content_type = http.parse_options_header(self.content_type)\n\n    @property\n    def content_type(self):\n        \"\"\"The content-type sent in the header.  Usually not available\"\"\"\n        return self.headers.get(\"content-type\")\n\n    @property\n    def content_length(self):\n        \"\"\"The content-length sent in the header.  Usually not available\"\"\"\n        try:\n            return _plain_int(self.headers.get(\"content-length\") or 0)\n        except ValueError:\n            return 0\n\n    @property\n    def mimetype(self):\n        \"\"\"Like :attr:`content_type`, but without parameters (eg, without\n        charset, type etc.) and always lowercase.  For example if the content\n        type is ``text/HTML; charset=utf-8`` the mimetype would be\n        ``'text/html'``.\n\n        .. versionadded:: 0.7\n        \"\"\"\n        self._parse_content_type()\n        return self._parsed_content_type[0].lower()\n\n    @property\n    def mimetype_params(self):\n        \"\"\"The mimetype parameters as dict.  For example if the content\n        type is ``text/html; charset=utf-8`` the params would be\n        ``{'charset': 'utf-8'}``.\n\n        .. versionadded:: 0.7\n        \"\"\"\n        self._parse_content_type()\n        return self._parsed_content_type[1]\n\n    def save(self, dst, buffer_size=16384):\n        \"\"\"Save the file to a destination path or file object.  If the\n        destination is a file object you have to close it yourself after the\n        call.  The buffer size is the number of bytes held in memory during\n        the copy process.  It defaults to 16KB.\n\n        For secure file saving also have a look at :func:`secure_filename`.\n\n        :param dst: a filename, :class:`os.PathLike`, or open file\n            object to write to.\n        :param buffer_size: Passed as the ``length`` parameter of\n            :func:`shutil.copyfileobj`.\n\n        .. versionchanged:: 1.0\n            Supports :mod:`pathlib`.\n        \"\"\"\n        from shutil import copyfileobj\n\n        close_dst = False\n\n        if hasattr(dst, \"__fspath__\"):\n            dst = fspath(dst)\n\n        if isinstance(dst, str):\n            dst = open(dst, \"wb\")\n            close_dst = True\n\n        try:\n            copyfileobj(self.stream, dst, buffer_size)\n        finally:\n            if close_dst:\n                dst.close()\n\n    def close(self):\n        \"\"\"Close the underlying file if possible.\"\"\"\n        try:\n            self.stream.close()\n        except Exception:\n            pass\n\n    def __bool__(self):\n        return bool(self.filename)\n\n    def __getattr__(self, name):\n        try:\n            return getattr(self.stream, name)\n        except AttributeError:\n            # SpooledTemporaryFile doesn't implement IOBase, get the\n            # attribute from its backing file instead.\n            # https://github.com/python/cpython/pull/3249\n            if hasattr(self.stream, \"_file\"):\n                return getattr(self.stream._file, name)\n            raise\n\n    def __iter__(self):\n        return iter(self.stream)\n\n    def __repr__(self):\n        return f\"<{type(self).__name__}: {self.filename!r} ({self.content_type!r})>\"\n\n\nclass FileMultiDict(MultiDict):\n    \"\"\"A special :class:`MultiDict` that has convenience methods to add\n    files to it.  This is used for :class:`EnvironBuilder` and generally\n    useful for unittesting.\n\n    .. versionadded:: 0.5\n    \"\"\"\n\n    def add_file(self, name, file, filename=None, content_type=None):\n        \"\"\"Adds a new file to the dict.  `file` can be a file name or\n        a :class:`file`-like or a :class:`FileStorage` object.\n\n        :param name: the name of the field.\n        :param file: a filename or :class:`file`-like object\n        :param filename: an optional filename\n        :param content_type: an optional content type\n        \"\"\"\n        if isinstance(file, FileStorage):\n            value = file\n        else:\n            if isinstance(file, str):\n                if filename is None:\n                    filename = file\n                file = open(file, \"rb\")\n            if filename and content_type is None:\n                content_type = (\n                    mimetypes.guess_type(filename)[0] or \"application/octet-stream\"\n                )\n            value = FileStorage(file, filename, name, content_type)\n\n        self.add(name, value)\n\n\n# circular dependencies\nfrom .. import http\n", "path": "src/werkzeug/datastructures/file_storage.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport mimetypes\nfrom io import BytesIO\nfrom os import fsdecode\nfrom os import fspath\n\nfrom .._internal import _plain_int\nfrom .structures import MultiDict\n\n\nclass FileStorage:\n    \"\"\"The :class:`FileStorage` class is a thin wrapper over incoming files.\n    It is used by the request object to represent uploaded files.  All the\n    attributes of the wrapper stream are proxied by the file storage so\n    it's possible to do ``storage.read()`` instead of the long form\n    ``storage.stream.read()``.\n    \"\"\"\n\n    def __init__(\n        self,\n        stream=None,\n        filename=None,\n        name=None,\n        content_type=None,\n        content_length=None,\n        headers=None,\n    ):\n        self.name = name\n        self.stream = stream or BytesIO()\n\n        # If no filename is provided, attempt to get the filename from\n        # the stream object. Python names special streams like\n        # ``<stderr>`` with angular brackets, skip these streams.\n        if filename is None:\n            filename = getattr(stream, \"name\", None)\n\n            if filename is not None:\n                filename = fsdecode(filename)\n\n            if filename and filename[0] == \"<\" and filename[-1] == \">\":\n                filename = None\n        else:\n            filename = fsdecode(filename)\n\n        self.filename = filename\n\n        if headers is None:\n            from .headers import Headers\n\n            headers = Headers()\n        self.headers = headers\n        if content_type is not None:\n            headers[\"Content-Type\"] = content_type\n        if content_length is not None:\n            headers[\"Content-Length\"] = str(content_length)\n\n    def _parse_content_type(self):\n        if not hasattr(self, \"_parsed_content_type\"):\n            self._parsed_content_type = http.parse_options_header(self.content_type)\n\n    @property\n    def content_type(self):\n        \"\"\"The content-type sent in the header.  Usually not available\"\"\"\n        return self.headers.get(\"content-type\")\n\n    @property\n    def content_length(self):\n        \"\"\"The content-length sent in the header.  Usually not available\"\"\"\n        if \"content-length\" in self.headers:\n            try:\n                return _plain_int(self.headers[\"content-length\"])\n            except ValueError:\n                pass\n\n        return 0\n\n    @property\n    def mimetype(self):\n        \"\"\"Like :attr:`content_type`, but without parameters (eg, without\n        charset, type etc.) and always lowercase.  For example if the content\n        type is ``text/HTML; charset=utf-8`` the mimetype would be\n        ``'text/html'``.\n\n        .. versionadded:: 0.7\n        \"\"\"\n        self._parse_content_type()\n        return self._parsed_content_type[0].lower()\n\n    @property\n    def mimetype_params(self):\n        \"\"\"The mimetype parameters as dict.  For example if the content\n        type is ``text/html; charset=utf-8`` the params would be\n        ``{'charset': 'utf-8'}``.\n\n        .. versionadded:: 0.7\n        \"\"\"\n        self._parse_content_type()\n        return self._parsed_content_type[1]\n\n    def save(self, dst, buffer_size=16384):\n        \"\"\"Save the file to a destination path or file object.  If the\n        destination is a file object you have to close it yourself after the\n        call.  The buffer size is the number of bytes held in memory during\n        the copy process.  It defaults to 16KB.\n\n        For secure file saving also have a look at :func:`secure_filename`.\n\n        :param dst: a filename, :class:`os.PathLike`, or open file\n            object to write to.\n        :param buffer_size: Passed as the ``length`` parameter of\n            :func:`shutil.copyfileobj`.\n\n        .. versionchanged:: 1.0\n            Supports :mod:`pathlib`.\n        \"\"\"\n        from shutil import copyfileobj\n\n        close_dst = False\n\n        if hasattr(dst, \"__fspath__\"):\n            dst = fspath(dst)\n\n        if isinstance(dst, str):\n            dst = open(dst, \"wb\")\n            close_dst = True\n\n        try:\n            copyfileobj(self.stream, dst, buffer_size)\n        finally:\n            if close_dst:\n                dst.close()\n\n    def close(self):\n        \"\"\"Close the underlying file if possible.\"\"\"\n        try:\n            self.stream.close()\n        except Exception:\n            pass\n\n    def __bool__(self):\n        return bool(self.filename)\n\n    def __getattr__(self, name):\n        try:\n            return getattr(self.stream, name)\n        except AttributeError:\n            # SpooledTemporaryFile doesn't implement IOBase, get the\n            # attribute from its backing file instead.\n            # https://github.com/python/cpython/pull/3249\n            if hasattr(self.stream, \"_file\"):\n                return getattr(self.stream._file, name)\n            raise\n\n    def __iter__(self):\n        return iter(self.stream)\n\n    def __repr__(self):\n        return f\"<{type(self).__name__}: {self.filename!r} ({self.content_type!r})>\"\n\n\nclass FileMultiDict(MultiDict):\n    \"\"\"A special :class:`MultiDict` that has convenience methods to add\n    files to it.  This is used for :class:`EnvironBuilder` and generally\n    useful for unittesting.\n\n    .. versionadded:: 0.5\n    \"\"\"\n\n    def add_file(self, name, file, filename=None, content_type=None):\n        \"\"\"Adds a new file to the dict.  `file` can be a file name or\n        a :class:`file`-like or a :class:`FileStorage` object.\n\n        :param name: the name of the field.\n        :param file: a filename or :class:`file`-like object\n        :param filename: an optional filename\n        :param content_type: an optional content type\n        \"\"\"\n        if isinstance(file, FileStorage):\n            value = file\n        else:\n            if isinstance(file, str):\n                if filename is None:\n                    filename = file\n                file = open(file, \"rb\")\n            if filename and content_type is None:\n                content_type = (\n                    mimetypes.guess_type(filename)[0] or \"application/octet-stream\"\n                )\n            value = FileStorage(file, filename, name, content_type)\n\n        self.add(name, value)\n\n\n# circular dependencies\nfrom .. import http\n", "path": "src/werkzeug/datastructures/file_storage.py"}]}