We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Multipart data parse regression
Seems that there is a regression in the new MultiPartPaser implementation

The following does not work in `Werkzeug==2.0.0`; but, works in  `Werkzeug<2.0.0`.

```
from werkzeug.formparser import MultiPartParser
from io import BytesIO

parser = MultiPartParser()

boundary = b'----------a_BoUnDaRy9009049739267083$'

f = BytesIO(b'------------a_BoUnDaRy9009049739267083$\r\nContent-Disposition: form-data; name="file"; filename="/home/marc/workspace/data-governance/tests/functional_tests/test_data_governance_api/controllers/work_unit_data/NaValues.csv"\r\nContent-Type: text/csv\r\n\r\n"I was told that I was not able to receive a pay increase because I was at the top of the pay range for my level. This indicates to me that I need to either be promoted to the next level, or look elsewhere for employment."\r\nProject Management seems inconsistent some are well run some are not transparent and all over the place. Hiring contractors also are not managed well and may have resulted to project overruns.\r\nNA\r\n"1. How can diversity and inclusion efforts just be tailored to minority group?2. Is it appropriate to use the term ""blacks"" when sending out a company email around diversity and inclusion?3. Again, an actual HR department would resolve this"\r\nNaN\r\n------------a_BoUnDaRy9009049739267083$--\r\n')

form, files = parser.parse(f, boundary, 965)
```

It results in the following error:

```

Traceback (most recent call last):
  File "/home/*/flaskapp_fail.py", line 57, in <module>
    form, files = parser.parse(f, boundary, 965)
  File "/home/*/lib/python3.8/site-packages/werkzeug/formparser.py", line 459, in parse
    event = parser.next_event()
  File "/home/*/lib/python3.8/site-packages/werkzeug/sansio/multipart.py", line 213, in next_event
    raise ValueError(f"Invalid form-data cannot parse beyond {self.state}")
ValueError: Invalid form-data cannot parse beyond State.PREAMBLE
```


Environment:

- Python version: 3.8.5
- Werkzeug version: 2.0.0


--- 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/sansio/multipart.py`
Content:
```
1 import re
2 from dataclasses import dataclass
3 from enum import auto
4 from enum import Enum
5 from typing import cast
6 from typing import List
7 from typing import Optional
8 from typing import Tuple
9 
10 from .._internal import _to_bytes
11 from .._internal import _to_str
12 from ..datastructures import Headers
13 from ..exceptions import RequestEntityTooLarge
14 from ..http import parse_options_header
15 
16 
17 class Event:
18     pass
19 
20 
21 @dataclass(frozen=True)
22 class Preamble(Event):
23     data: bytes
24 
25 
26 @dataclass(frozen=True)
27 class Field(Event):
28     name: str
29     headers: Headers
30 
31 
32 @dataclass(frozen=True)
33 class File(Event):
34     name: str
35     filename: str
36     headers: Headers
37 
38 
39 @dataclass(frozen=True)
40 class Data(Event):
41     data: bytes
42     more_data: bool
43 
44 
45 @dataclass(frozen=True)
46 class Epilogue(Event):
47     data: bytes
48 
49 
50 class NeedData(Event):
51     pass
52 
53 
54 NEED_DATA = NeedData()
55 
56 
57 class State(Enum):
58     PREAMBLE = auto()
59     PART = auto()
60     DATA = auto()
61     EPILOGUE = auto()
62     COMPLETE = auto()
63 
64 
65 # Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that
66 # many implementations break this and either use CR or LF alone.
67 LINE_BREAK = b"(?:\r\n|\n|\r)"
68 BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE)
69 LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)
70 # Header values can be continued via a space or tab after the linebreak, as
71 # per RFC2231
72 HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE)
73 
74 
75 class MultipartDecoder:
76     """Decodes a multipart message as bytes into Python events.
77 
78     The part data is returned as available to allow the caller to save
79     the data from memory to disk, if desired.
80     """
81 
82     def __init__(
83         self,
84         boundary: bytes,
85         max_form_memory_size: Optional[int] = None,
86     ) -> None:
87         self.buffer = bytearray()
88         self.complete = False
89         self.max_form_memory_size = max_form_memory_size
90         self.state = State.PREAMBLE
91         self.boundary = boundary
92 
93         # Note in the below \h i.e. horizontal whitespace is used
94         # as [^\S\n\r] as \h isn't supported in python.
95 
96         # The preamble must end with a boundary where the boundary is
97         # prefixed by a line break, RFC2046. Except that many
98         # implementations including Werkzeug's tests omit the line
99         # break prefix. In addition the first boundary could be the
100         # epilogue boundary (for empty form-data) hence the matching
101         # group to understand if it is an epilogue boundary.
102         self.preamble_re = re.compile(
103             br"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
104             % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),
105             re.MULTILINE,
106         )
107         # A boundary must include a line break prefix and suffix, and
108         # may include trailing whitespace. In addition the boundary
109         # could be the epilogue boundary hence the matching group to
110         # understand if it is an epilogue boundary.
111         self.boundary_re = re.compile(
112             br"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
113             % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),
114             re.MULTILINE,
115         )
116 
117     def last_newline(self) -> int:
118         try:
119             last_nl = self.buffer.rindex(b"\n")
120         except ValueError:
121             last_nl = len(self.buffer)
122         try:
123             last_cr = self.buffer.rindex(b"\r")
124         except ValueError:
125             last_cr = len(self.buffer)
126 
127         return min(last_nl, last_cr)
128 
129     def receive_data(self, data: Optional[bytes]) -> None:
130         if data is None:
131             self.complete = True
132         elif (
133             self.max_form_memory_size is not None
134             and len(self.buffer) + len(data) > self.max_form_memory_size
135         ):
136             raise RequestEntityTooLarge()
137         else:
138             self.buffer.extend(data)
139 
140     def next_event(self) -> Event:
141         event: Event = NEED_DATA
142 
143         if self.state == State.PREAMBLE:
144             match = self.preamble_re.search(self.buffer)
145             if match is not None:
146                 if match.group(1).startswith(b"--"):
147                     self.state = State.EPILOGUE
148                 else:
149                     self.state = State.PART
150                 data = bytes(self.buffer[: match.start()])
151                 del self.buffer[: match.end()]
152                 event = Preamble(data=data)
153 
154         elif self.state == State.PART:
155             match = BLANK_LINE_RE.search(self.buffer)
156             if match is not None:
157                 headers = self._parse_headers(self.buffer[: match.start()])
158                 del self.buffer[: match.end()]
159 
160                 if "content-disposition" not in headers:
161                     raise ValueError("Missing Content-Disposition header")
162 
163                 disposition, extra = parse_options_header(
164                     headers["content-disposition"]
165                 )
166                 name = cast(str, extra.get("name"))
167                 filename = extra.get("filename")
168                 if filename is not None:
169                     event = File(
170                         filename=filename,
171                         headers=headers,
172                         name=name,
173                     )
174                 else:
175                     event = Field(
176                         headers=headers,
177                         name=name,
178                     )
179                 self.state = State.DATA
180 
181         elif self.state == State.DATA:
182             if self.buffer.find(b"--" + self.boundary) == -1:
183                 # No complete boundary in the buffer, but there may be
184                 # a partial boundary at the end. As the boundary
185                 # starts with either a nl or cr find the earliest and
186                 # return up to that as data.
187                 data_length = del_index = self.last_newline()
188                 more_data = True
189             else:
190                 match = self.boundary_re.search(self.buffer)
191                 if match is not None:
192                     if match.group(1).startswith(b"--"):
193                         self.state = State.EPILOGUE
194                     else:
195                         self.state = State.PART
196                     data_length = match.start()
197                     del_index = match.end()
198                 else:
199                     data_length = del_index = self.last_newline()
200                 more_data = match is None
201 
202             data = bytes(self.buffer[:data_length])
203             del self.buffer[:del_index]
204             if data or not more_data:
205                 event = Data(data=data, more_data=more_data)
206 
207         elif self.state == State.EPILOGUE and self.complete:
208             event = Epilogue(data=bytes(self.buffer))
209             del self.buffer[:]
210             self.state = State.COMPLETE
211 
212         if self.complete and isinstance(event, NeedData):
213             raise ValueError(f"Invalid form-data cannot parse beyond {self.state}")
214 
215         return event
216 
217     def _parse_headers(self, data: bytes) -> Headers:
218         headers: List[Tuple[str, str]] = []
219         # Merge the continued headers into one line
220         data = HEADER_CONTINUATION_RE.sub(b" ", data)
221         # Now there is one header per line
222         for line in data.splitlines():
223             if line.strip() != b"":
224                 name, value = _to_str(line).strip().split(":", 1)
225                 headers.append((name.strip(), value.strip()))
226         return Headers(headers)
227 
228 
229 class MultipartEncoder:
230     def __init__(self, boundary: bytes) -> None:
231         self.boundary = boundary
232         self.state = State.PREAMBLE
233 
234     def send_event(self, event: Event) -> bytes:
235         if isinstance(event, Preamble) and self.state == State.PREAMBLE:
236             self.state = State.PART
237             return event.data
238         elif isinstance(event, (Field, File)) and self.state in {
239             State.PREAMBLE,
240             State.PART,
241             State.DATA,
242         }:
243             self.state = State.DATA
244             data = b"\r\n--" + self.boundary + b"\r\n"
245             data += b'Content-Disposition: form-data; name="%s"' % _to_bytes(event.name)
246             if isinstance(event, File):
247                 data += b'; filename="%s"' % _to_bytes(event.filename)
248             data += b"\r\n"
249             for name, value in cast(Field, event).headers:
250                 if name.lower() != "content-disposition":
251                     data += _to_bytes(f"{name}: {value}\r\n")
252             data += b"\r\n"
253             return data
254         elif isinstance(event, Data) and self.state == State.DATA:
255             return event.data
256         elif isinstance(event, Epilogue):
257             self.state = State.COMPLETE
258             return b"\r\n--" + self.boundary + b"--\r\n" + event.data
259         else:
260             raise ValueError(f"Cannot generate {event} in state: {self.state}")
261
```
--- 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/sansio/multipart.py b/src/werkzeug/sansio/multipart.py
--- a/src/werkzeug/sansio/multipart.py
+++ b/src/werkzeug/sansio/multipart.py
@@ -101,7 +101,7 @@
         # group to understand if it is an epilogue boundary.
         self.preamble_re = re.compile(
             br"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
-            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),
+            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),
             re.MULTILINE,
         )
         # A boundary must include a line break prefix and suffix, and
@@ -110,7 +110,7 @@
         # understand if it is an epilogue boundary.
         self.boundary_re = re.compile(
             br"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)"
-            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),
+            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),
             re.MULTILINE,
         )

{"golden_diff": "diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py\n--- a/src/werkzeug/sansio/multipart.py\n+++ b/src/werkzeug/sansio/multipart.py\n@@ -101,7 +101,7 @@\n         # group to understand if it is an epilogue boundary.\n         self.preamble_re = re.compile(\n             br\"%s?--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n-            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),\n+            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n             re.MULTILINE,\n         )\n         # A boundary must include a line break prefix and suffix, and\n@@ -110,7 +110,7 @@\n         # understand if it is an epilogue boundary.\n         self.boundary_re = re.compile(\n             br\"%s--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n-            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),\n+            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n             re.MULTILINE,\n         )\n", "issue": "[BUG] Multipart data parse regression\nSeems that there is a regression in the new MultiPartPaser implementation\r\n\r\nThe following does not work in `Werkzeug==2.0.0`; but, works in  `Werkzeug<2.0.0`.\r\n\r\n```\r\nfrom werkzeug.formparser import MultiPartParser\r\nfrom io import BytesIO\r\n\r\nparser = MultiPartParser()\r\n\r\nboundary = b'----------a_BoUnDaRy9009049739267083$'\r\n\r\nf = BytesIO(b'------------a_BoUnDaRy9009049739267083$\\r\\nContent-Disposition: form-data; name=\"file\"; filename=\"/home/marc/workspace/data-governance/tests/functional_tests/test_data_governance_api/controllers/work_unit_data/NaValues.csv\"\\r\\nContent-Type: text/csv\\r\\n\\r\\n\"I was told that I was not able to receive a pay increase because I was at the top of the pay range for my level. This indicates to me that I need to either be promoted to the next level, or look elsewhere for employment.\"\\r\\nProject Management seems inconsistent some are well run some are not transparent and all over the place. Hiring contractors also are not managed well and may have resulted to project overruns.\\r\\nNA\\r\\n\"1. How can diversity and inclusion efforts just be tailored to minority group?2. Is it appropriate to use the term \"\"blacks\"\" when sending out a company email around diversity and inclusion?3. Again, an actual HR department would resolve this\"\\r\\nNaN\\r\\n------------a_BoUnDaRy9009049739267083$--\\r\\n')\r\n\r\nform, files = parser.parse(f, boundary, 965)\r\n```\r\n\r\nIt results in the following error:\r\n\r\n```\r\n\r\nTraceback (most recent call last):\r\n  File \"/home/*/flaskapp_fail.py\", line 57, in <module>\r\n    form, files = parser.parse(f, boundary, 965)\r\n  File \"/home/*/lib/python3.8/site-packages/werkzeug/formparser.py\", line 459, in parse\r\n    event = parser.next_event()\r\n  File \"/home/*/lib/python3.8/site-packages/werkzeug/sansio/multipart.py\", line 213, in next_event\r\n    raise ValueError(f\"Invalid form-data cannot parse beyond {self.state}\")\r\nValueError: Invalid form-data cannot parse beyond State.PREAMBLE\r\n```\r\n\r\n\r\nEnvironment:\r\n\r\n- Python version: 3.8.5\r\n- Werkzeug version: 2.0.0\r\n\n", "before_files": [{"content": "import re\nfrom dataclasses import dataclass\nfrom enum import auto\nfrom enum import Enum\nfrom typing import cast\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\n\nfrom .._internal import _to_bytes\nfrom .._internal import _to_str\nfrom ..datastructures import Headers\nfrom ..exceptions import RequestEntityTooLarge\nfrom ..http import parse_options_header\n\n\nclass Event:\n    pass\n\n\n@dataclass(frozen=True)\nclass Preamble(Event):\n    data: bytes\n\n\n@dataclass(frozen=True)\nclass Field(Event):\n    name: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass File(Event):\n    name: str\n    filename: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass Data(Event):\n    data: bytes\n    more_data: bool\n\n\n@dataclass(frozen=True)\nclass Epilogue(Event):\n    data: bytes\n\n\nclass NeedData(Event):\n    pass\n\n\nNEED_DATA = NeedData()\n\n\nclass State(Enum):\n    PREAMBLE = auto()\n    PART = auto()\n    DATA = auto()\n    EPILOGUE = auto()\n    COMPLETE = auto()\n\n\n# Multipart line breaks MUST be CRLF (\\r\\n) by RFC-7578, except that\n# many implementations break this and either use CR or LF alone.\nLINE_BREAK = b\"(?:\\r\\n|\\n|\\r)\"\nBLANK_LINE_RE = re.compile(b\"(?:\\r\\n\\r\\n|\\r\\r|\\n\\n)\", re.MULTILINE)\nLINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)\n# Header values can be continued via a space or tab after the linebreak, as\n# per RFC2231\nHEADER_CONTINUATION_RE = re.compile(b\"%s[ \\t]\" % LINE_BREAK, re.MULTILINE)\n\n\nclass MultipartDecoder:\n    \"\"\"Decodes a multipart message as bytes into Python events.\n\n    The part data is returned as available to allow the caller to save\n    the data from memory to disk, if desired.\n    \"\"\"\n\n    def __init__(\n        self,\n        boundary: bytes,\n        max_form_memory_size: Optional[int] = None,\n    ) -> None:\n        self.buffer = bytearray()\n        self.complete = False\n        self.max_form_memory_size = max_form_memory_size\n        self.state = State.PREAMBLE\n        self.boundary = boundary\n\n        # Note in the below \\h i.e. horizontal whitespace is used\n        # as [^\\S\\n\\r] as \\h isn't supported in python.\n\n        # The preamble must end with a boundary where the boundary is\n        # prefixed by a line break, RFC2046. Except that many\n        # implementations including Werkzeug's tests omit the line\n        # break prefix. In addition the first boundary could be the\n        # epilogue boundary (for empty form-data) hence the matching\n        # group to understand if it is an epilogue boundary.\n        self.preamble_re = re.compile(\n            br\"%s?--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        # A boundary must include a line break prefix and suffix, and\n        # may include trailing whitespace. In addition the boundary\n        # could be the epilogue boundary hence the matching group to\n        # understand if it is an epilogue boundary.\n        self.boundary_re = re.compile(\n            br\"%s--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, boundary, LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n\n    def last_newline(self) -> int:\n        try:\n            last_nl = self.buffer.rindex(b\"\\n\")\n        except ValueError:\n            last_nl = len(self.buffer)\n        try:\n            last_cr = self.buffer.rindex(b\"\\r\")\n        except ValueError:\n            last_cr = len(self.buffer)\n\n        return min(last_nl, last_cr)\n\n    def receive_data(self, data: Optional[bytes]) -> None:\n        if data is None:\n            self.complete = True\n        elif (\n            self.max_form_memory_size is not None\n            and len(self.buffer) + len(data) > self.max_form_memory_size\n        ):\n            raise RequestEntityTooLarge()\n        else:\n            self.buffer.extend(data)\n\n    def next_event(self) -> Event:\n        event: Event = NEED_DATA\n\n        if self.state == State.PREAMBLE:\n            match = self.preamble_re.search(self.buffer)\n            if match is not None:\n                if match.group(1).startswith(b\"--\"):\n                    self.state = State.EPILOGUE\n                else:\n                    self.state = State.PART\n                data = bytes(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n                event = Preamble(data=data)\n\n        elif self.state == State.PART:\n            match = BLANK_LINE_RE.search(self.buffer)\n            if match is not None:\n                headers = self._parse_headers(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n\n                if \"content-disposition\" not in headers:\n                    raise ValueError(\"Missing Content-Disposition header\")\n\n                disposition, extra = parse_options_header(\n                    headers[\"content-disposition\"]\n                )\n                name = cast(str, extra.get(\"name\"))\n                filename = extra.get(\"filename\")\n                if filename is not None:\n                    event = File(\n                        filename=filename,\n                        headers=headers,\n                        name=name,\n                    )\n                else:\n                    event = Field(\n                        headers=headers,\n                        name=name,\n                    )\n                self.state = State.DATA\n\n        elif self.state == State.DATA:\n            if self.buffer.find(b\"--\" + self.boundary) == -1:\n                # No complete boundary in the buffer, but there may be\n                # a partial boundary at the end. As the boundary\n                # starts with either a nl or cr find the earliest and\n                # return up to that as data.\n                data_length = del_index = self.last_newline()\n                more_data = True\n            else:\n                match = self.boundary_re.search(self.buffer)\n                if match is not None:\n                    if match.group(1).startswith(b\"--\"):\n                        self.state = State.EPILOGUE\n                    else:\n                        self.state = State.PART\n                    data_length = match.start()\n                    del_index = match.end()\n                else:\n                    data_length = del_index = self.last_newline()\n                more_data = match is None\n\n            data = bytes(self.buffer[:data_length])\n            del self.buffer[:del_index]\n            if data or not more_data:\n                event = Data(data=data, more_data=more_data)\n\n        elif self.state == State.EPILOGUE and self.complete:\n            event = Epilogue(data=bytes(self.buffer))\n            del self.buffer[:]\n            self.state = State.COMPLETE\n\n        if self.complete and isinstance(event, NeedData):\n            raise ValueError(f\"Invalid form-data cannot parse beyond {self.state}\")\n\n        return event\n\n    def _parse_headers(self, data: bytes) -> Headers:\n        headers: List[Tuple[str, str]] = []\n        # Merge the continued headers into one line\n        data = HEADER_CONTINUATION_RE.sub(b\" \", data)\n        # Now there is one header per line\n        for line in data.splitlines():\n            if line.strip() != b\"\":\n                name, value = _to_str(line).strip().split(\":\", 1)\n                headers.append((name.strip(), value.strip()))\n        return Headers(headers)\n\n\nclass MultipartEncoder:\n    def __init__(self, boundary: bytes) -> None:\n        self.boundary = boundary\n        self.state = State.PREAMBLE\n\n    def send_event(self, event: Event) -> bytes:\n        if isinstance(event, Preamble) and self.state == State.PREAMBLE:\n            self.state = State.PART\n            return event.data\n        elif isinstance(event, (Field, File)) and self.state in {\n            State.PREAMBLE,\n            State.PART,\n            State.DATA,\n        }:\n            self.state = State.DATA\n            data = b\"\\r\\n--\" + self.boundary + b\"\\r\\n\"\n            data += b'Content-Disposition: form-data; name=\"%s\"' % _to_bytes(event.name)\n            if isinstance(event, File):\n                data += b'; filename=\"%s\"' % _to_bytes(event.filename)\n            data += b\"\\r\\n\"\n            for name, value in cast(Field, event).headers:\n                if name.lower() != \"content-disposition\":\n                    data += _to_bytes(f\"{name}: {value}\\r\\n\")\n            data += b\"\\r\\n\"\n            return data\n        elif isinstance(event, Data) and self.state == State.DATA:\n            return event.data\n        elif isinstance(event, Epilogue):\n            self.state = State.COMPLETE\n            return b\"\\r\\n--\" + self.boundary + b\"--\\r\\n\" + event.data\n        else:\n            raise ValueError(f\"Cannot generate {event} in state: {self.state}\")\n", "path": "src/werkzeug/sansio/multipart.py"}], "after_files": [{"content": "import re\nfrom dataclasses import dataclass\nfrom enum import auto\nfrom enum import Enum\nfrom typing import cast\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\n\nfrom .._internal import _to_bytes\nfrom .._internal import _to_str\nfrom ..datastructures import Headers\nfrom ..exceptions import RequestEntityTooLarge\nfrom ..http import parse_options_header\n\n\nclass Event:\n    pass\n\n\n@dataclass(frozen=True)\nclass Preamble(Event):\n    data: bytes\n\n\n@dataclass(frozen=True)\nclass Field(Event):\n    name: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass File(Event):\n    name: str\n    filename: str\n    headers: Headers\n\n\n@dataclass(frozen=True)\nclass Data(Event):\n    data: bytes\n    more_data: bool\n\n\n@dataclass(frozen=True)\nclass Epilogue(Event):\n    data: bytes\n\n\nclass NeedData(Event):\n    pass\n\n\nNEED_DATA = NeedData()\n\n\nclass State(Enum):\n    PREAMBLE = auto()\n    PART = auto()\n    DATA = auto()\n    EPILOGUE = auto()\n    COMPLETE = auto()\n\n\n# Multipart line breaks MUST be CRLF (\\r\\n) by RFC-7578, except that\n# many implementations break this and either use CR or LF alone.\nLINE_BREAK = b\"(?:\\r\\n|\\n|\\r)\"\nBLANK_LINE_RE = re.compile(b\"(?:\\r\\n\\r\\n|\\r\\r|\\n\\n)\", re.MULTILINE)\nLINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE)\n# Header values can be continued via a space or tab after the linebreak, as\n# per RFC2231\nHEADER_CONTINUATION_RE = re.compile(b\"%s[ \\t]\" % LINE_BREAK, re.MULTILINE)\n\n\nclass MultipartDecoder:\n    \"\"\"Decodes a multipart message as bytes into Python events.\n\n    The part data is returned as available to allow the caller to save\n    the data from memory to disk, if desired.\n    \"\"\"\n\n    def __init__(\n        self,\n        boundary: bytes,\n        max_form_memory_size: Optional[int] = None,\n    ) -> None:\n        self.buffer = bytearray()\n        self.complete = False\n        self.max_form_memory_size = max_form_memory_size\n        self.state = State.PREAMBLE\n        self.boundary = boundary\n\n        # Note in the below \\h i.e. horizontal whitespace is used\n        # as [^\\S\\n\\r] as \\h isn't supported in python.\n\n        # The preamble must end with a boundary where the boundary is\n        # prefixed by a line break, RFC2046. Except that many\n        # implementations including Werkzeug's tests omit the line\n        # break prefix. In addition the first boundary could be the\n        # epilogue boundary (for empty form-data) hence the matching\n        # group to understand if it is an epilogue boundary.\n        self.preamble_re = re.compile(\n            br\"%s?--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n        # A boundary must include a line break prefix and suffix, and\n        # may include trailing whitespace. In addition the boundary\n        # could be the epilogue boundary hence the matching group to\n        # understand if it is an epilogue boundary.\n        self.boundary_re = re.compile(\n            br\"%s--%s(--[^\\S\\n\\r]*%s?|[^\\S\\n\\r]*%s)\"\n            % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK),\n            re.MULTILINE,\n        )\n\n    def last_newline(self) -> int:\n        try:\n            last_nl = self.buffer.rindex(b\"\\n\")\n        except ValueError:\n            last_nl = len(self.buffer)\n        try:\n            last_cr = self.buffer.rindex(b\"\\r\")\n        except ValueError:\n            last_cr = len(self.buffer)\n\n        return min(last_nl, last_cr)\n\n    def receive_data(self, data: Optional[bytes]) -> None:\n        if data is None:\n            self.complete = True\n        elif (\n            self.max_form_memory_size is not None\n            and len(self.buffer) + len(data) > self.max_form_memory_size\n        ):\n            raise RequestEntityTooLarge()\n        else:\n            self.buffer.extend(data)\n\n    def next_event(self) -> Event:\n        event: Event = NEED_DATA\n\n        if self.state == State.PREAMBLE:\n            match = self.preamble_re.search(self.buffer)\n            if match is not None:\n                if match.group(1).startswith(b\"--\"):\n                    self.state = State.EPILOGUE\n                else:\n                    self.state = State.PART\n                data = bytes(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n                event = Preamble(data=data)\n\n        elif self.state == State.PART:\n            match = BLANK_LINE_RE.search(self.buffer)\n            if match is not None:\n                headers = self._parse_headers(self.buffer[: match.start()])\n                del self.buffer[: match.end()]\n\n                if \"content-disposition\" not in headers:\n                    raise ValueError(\"Missing Content-Disposition header\")\n\n                disposition, extra = parse_options_header(\n                    headers[\"content-disposition\"]\n                )\n                name = cast(str, extra.get(\"name\"))\n                filename = extra.get(\"filename\")\n                if filename is not None:\n                    event = File(\n                        filename=filename,\n                        headers=headers,\n                        name=name,\n                    )\n                else:\n                    event = Field(\n                        headers=headers,\n                        name=name,\n                    )\n                self.state = State.DATA\n\n        elif self.state == State.DATA:\n            if self.buffer.find(b\"--\" + self.boundary) == -1:\n                # No complete boundary in the buffer, but there may be\n                # a partial boundary at the end. As the boundary\n                # starts with either a nl or cr find the earliest and\n                # return up to that as data.\n                data_length = del_index = self.last_newline()\n                more_data = True\n            else:\n                match = self.boundary_re.search(self.buffer)\n                if match is not None:\n                    if match.group(1).startswith(b\"--\"):\n                        self.state = State.EPILOGUE\n                    else:\n                        self.state = State.PART\n                    data_length = match.start()\n                    del_index = match.end()\n                else:\n                    data_length = del_index = self.last_newline()\n                more_data = match is None\n\n            data = bytes(self.buffer[:data_length])\n            del self.buffer[:del_index]\n            if data or not more_data:\n                event = Data(data=data, more_data=more_data)\n\n        elif self.state == State.EPILOGUE and self.complete:\n            event = Epilogue(data=bytes(self.buffer))\n            del self.buffer[:]\n            self.state = State.COMPLETE\n\n        if self.complete and isinstance(event, NeedData):\n            raise ValueError(f\"Invalid form-data cannot parse beyond {self.state}\")\n\n        return event\n\n    def _parse_headers(self, data: bytes) -> Headers:\n        headers: List[Tuple[str, str]] = []\n        # Merge the continued headers into one line\n        data = HEADER_CONTINUATION_RE.sub(b\" \", data)\n        # Now there is one header per line\n        for line in data.splitlines():\n            if line.strip() != b\"\":\n                name, value = _to_str(line).strip().split(\":\", 1)\n                headers.append((name.strip(), value.strip()))\n        return Headers(headers)\n\n\nclass MultipartEncoder:\n    def __init__(self, boundary: bytes) -> None:\n        self.boundary = boundary\n        self.state = State.PREAMBLE\n\n    def send_event(self, event: Event) -> bytes:\n        if isinstance(event, Preamble) and self.state == State.PREAMBLE:\n            self.state = State.PART\n            return event.data\n        elif isinstance(event, (Field, File)) and self.state in {\n            State.PREAMBLE,\n            State.PART,\n            State.DATA,\n        }:\n            self.state = State.DATA\n            data = b\"\\r\\n--\" + self.boundary + b\"\\r\\n\"\n            data += b'Content-Disposition: form-data; name=\"%s\"' % _to_bytes(event.name)\n            if isinstance(event, File):\n                data += b'; filename=\"%s\"' % _to_bytes(event.filename)\n            data += b\"\\r\\n\"\n            for name, value in cast(Field, event).headers:\n                if name.lower() != \"content-disposition\":\n                    data += _to_bytes(f\"{name}: {value}\\r\\n\")\n            data += b\"\\r\\n\"\n            return data\n        elif isinstance(event, Data) and self.state == State.DATA:\n            return event.data\n        elif isinstance(event, Epilogue):\n            self.state = State.COMPLETE\n            return b\"\\r\\n--\" + self.boundary + b\"--\\r\\n\" + event.data\n        else:\n            raise ValueError(f\"Cannot generate {event} in state: {self.state}\")\n", "path": "src/werkzeug/sansio/multipart.py"}]}