prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add `allow_origin_regex` to CORSMiddleware.
  It'd be helpful if `CORSMiddleware` supported an `allow_origin_regex`, so that users could do...
  
  ```python
  # Enforce a subdomain CORS policy
  app.add_middleware(CORSMiddleware, allow_origin_regex="(http|https)://*.example.com")
  ```
  
  Or...
  
  ```python
  # Enforce an HTTPS-only CORS policy.
  app.add_middleware(CORSMiddleware, allow_origin_regex="https://*")
  ```
  
  The string should be compiled to a regex by the middleware and matches should be anchored to the start/end of the origin string.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `starlette/middleware/cors.py`
  Content:
  ```
  1 from starlette.datastructures import Headers, MutableHeaders, URL
  2 from starlette.responses import PlainTextResponse
  3 from starlette.types import ASGIApp, ASGIInstance, Scope
  4 import functools
  5 import typing
  6 
  7 
  8 ALL_METHODS = ("DELETE", "GET", "OPTIONS", "PATCH", "POST", "PUT")
  9 
  10 
  11 class CORSMiddleware:
  12     def __init__(
  13         self,
  14         app: ASGIApp,
  15         allow_origins: typing.Sequence[str] = (),
  16         allow_methods: typing.Sequence[str] = ("GET",),
  17         allow_headers: typing.Sequence[str] = (),
  18         allow_credentials: bool = False,
  19         expose_headers: typing.Sequence[str] = (),
  20         max_age: int = 600,
  21     ):
  22 
  23         if "*" in allow_methods:
  24             allow_methods = ALL_METHODS
  25 
  26         simple_headers = {}
  27         if "*" in allow_origins:
  28             simple_headers["Access-Control-Allow-Origin"] = "*"
  29         if allow_credentials:
  30             simple_headers["Access-Control-Allow-Credentials"] = "true"
  31         if expose_headers:
  32             simple_headers["Access-Control-Expose-Headers"] = ", ".join(expose_headers)
  33 
  34         preflight_headers = {}
  35         if "*" in allow_origins:
  36             preflight_headers["Access-Control-Allow-Origin"] = "*"
  37         else:
  38             preflight_headers["Vary"] = "Origin"
  39         preflight_headers.update(
  40             {
  41                 "Access-Control-Allow-Methods": ", ".join(allow_methods),
  42                 "Access-Control-Max-Age": str(max_age),
  43             }
  44         )
  45         if allow_headers and "*" not in allow_headers:
  46             preflight_headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers)
  47         if allow_credentials:
  48             preflight_headers["Access-Control-Allow-Credentials"] = "true"
  49 
  50         self.app = app
  51         self.allow_origins = allow_origins
  52         self.allow_methods = allow_methods
  53         self.allow_headers = allow_headers
  54         self.allow_all_origins = "*" in allow_origins
  55         self.allow_all_headers = "*" in allow_headers
  56         self.simple_headers = simple_headers
  57         self.preflight_headers = preflight_headers
  58 
  59     def __call__(self, scope: Scope):
  60         if scope["type"] == "http":
  61             method = scope["method"]
  62             headers = Headers(scope["headers"])
  63             origin = headers.get("origin")
  64 
  65             if origin is not None:
  66                 if method == "OPTIONS" and "access-control-request-method" in headers:
  67                     return self.preflight_response(request_headers=headers)
  68                 else:
  69                     return functools.partial(
  70                         self.simple_response, scope=scope, origin=origin
  71                     )
  72 
  73         return self.app(scope)
  74 
  75     def preflight_response(self, request_headers):
  76         requested_origin = request_headers["origin"]
  77         requested_method = request_headers["access-control-request-method"]
  78         requested_headers = request_headers.get("access-control-request-headers")
  79         requested_cookie = "cookie" in request_headers
  80 
  81         headers = dict(self.preflight_headers)
  82         failures = []
  83 
  84         # If we only allow specific origins, then we have to mirror back
  85         # the Origin header in the response.
  86         if not self.allow_all_origins:
  87             if requested_origin in self.allow_origins:
  88                 headers["Access-Control-Allow-Origin"] = requested_origin
  89             else:
  90                 failures.append("origin")
  91 
  92         if requested_method not in self.allow_methods:
  93             failures.append("method")
  94 
  95         # If we allow all headers, then we have to mirror back any requested
  96         # headers in the response.
  97         if self.allow_all_headers and requested_headers is not None:
  98             headers["Access-Control-Allow-Headers"] = requested_headers
  99         elif requested_headers is not None:
  100             for header in requested_headers.split(","):
  101                 if header.strip() not in self.allow_headers:
  102                     failures.append("headers")
  103 
  104         # We don't strictly need to use 400 responses here, since its up to
  105         # the browser to enforce the CORS policy, but its more informative
  106         # if we do.
  107         if failures:
  108             failure_text = "Disallowed CORS " + ", ".join(failures)
  109             return PlainTextResponse(failure_text, status_code=400, headers=headers)
  110 
  111         return PlainTextResponse("OK", status_code=200, headers=headers)
  112 
  113     async def simple_response(self, receive, send, scope=None, origin=None):
  114         inner = self.app(scope)
  115         send = functools.partial(self.send, send=send, origin=origin)
  116         await inner(receive, send)
  117 
  118     async def send(self, message, send=None, origin=None):
  119         if message["type"] != "http.response.start":
  120             await send(message)
  121             return
  122 
  123         message.setdefault("headers", [])
  124         headers = MutableHeaders(message["headers"])
  125 
  126         # If we only allow specific origins, then we have to mirror back
  127         # the Origin header in the response.
  128         if not self.allow_all_origins and origin in self.allow_origins:
  129             headers["Access-Control-Allow-Origin"] = origin
  130         headers.update(self.simple_headers)
  131         await send(message)
  132
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/starlette/middleware/cors.py b/starlette/middleware/cors.py
  --- a/starlette/middleware/cors.py
  +++ b/starlette/middleware/cors.py
  @@ -3,6 +3,7 @@
   from starlette.types import ASGIApp, ASGIInstance, Scope
   import functools
   import typing
  +import re
   
   
   ALL_METHODS = ("DELETE", "GET", "OPTIONS", "PATCH", "POST", "PUT")
  @@ -16,6 +17,7 @@
           allow_methods: typing.Sequence[str] = ("GET",),
           allow_headers: typing.Sequence[str] = (),
           allow_credentials: bool = False,
  +        allow_origin_regex: str = None,
           expose_headers: typing.Sequence[str] = (),
           max_age: int = 600,
       ):
  @@ -23,6 +25,10 @@
           if "*" in allow_methods:
               allow_methods = ALL_METHODS
   
  +        if allow_origin_regex is not None:
  +            regex = re.compile(allow_origin_regex)
  +            allow_origin_regex = regex
  +
           simple_headers = {}
           if "*" in allow_origins:
               simple_headers["Access-Control-Allow-Origin"] = "*"
  @@ -53,6 +59,7 @@
           self.allow_headers = allow_headers
           self.allow_all_origins = "*" in allow_origins
           self.allow_all_headers = "*" in allow_headers
  +        self.allow_origin_regex = allow_origin_regex
           self.simple_headers = simple_headers
           self.preflight_headers = preflight_headers
   
  @@ -66,12 +73,22 @@
                   if method == "OPTIONS" and "access-control-request-method" in headers:
                       return self.preflight_response(request_headers=headers)
                   else:
  -                    return functools.partial(
  -                        self.simple_response, scope=scope, origin=origin
  -                    )
  +                    if self.is_allowed_origin(origin=origin):
  +                        return functools.partial(
  +                            self.simple_response, scope=scope, origin=origin
  +                        )
  +                    return PlainTextResponse("Disallowed CORS origin", status_code=400)
   
           return self.app(scope)
   
  +    def is_allowed_origin(self, origin):
  +        if self.allow_origin_regex:
  +            return self.allow_origin_regex.match(origin)
  +        if self.allow_all_origins:
  +            return True
  +
  +        return origin in self.allow_origins
  +
       def preflight_response(self, request_headers):
           requested_origin = request_headers["origin"]
           requested_method = request_headers["access-control-request-method"]
  @@ -84,7 +101,7 @@
           # If we only allow specific origins, then we have to mirror back
           # the Origin header in the response.
           if not self.allow_all_origins:
  -            if requested_origin in self.allow_origins:
  +            if self.is_allowed_origin(origin=requested_origin):
                   headers["Access-Control-Allow-Origin"] = requested_origin
               else:
                   failures.append("origin")
  @@ -125,7 +142,7 @@
   
           # If we only allow specific origins, then we have to mirror back
           # the Origin header in the response.
  -        if not self.allow_all_origins and origin in self.allow_origins:
  +        if not self.allow_all_origins and self.is_allowed_origin(origin=origin):
               headers["Access-Control-Allow-Origin"] = origin
           headers.update(self.simple_headers)
           await send(message)
  
verification_info: {"golden_diff": "diff --git a/starlette/middleware/cors.py b/starlette/middleware/cors.py\n--- a/starlette/middleware/cors.py\n+++ b/starlette/middleware/cors.py\n@@ -3,6 +3,7 @@\n from starlette.types import ASGIApp, ASGIInstance, Scope\n import functools\n import typing\n+import re\n \n \n ALL_METHODS = (\"DELETE\", \"GET\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\")\n@@ -16,6 +17,7 @@\n         allow_methods: typing.Sequence[str] = (\"GET\",),\n         allow_headers: typing.Sequence[str] = (),\n         allow_credentials: bool = False,\n+        allow_origin_regex: str = None,\n         expose_headers: typing.Sequence[str] = (),\n         max_age: int = 600,\n     ):\n@@ -23,6 +25,10 @@\n         if \"*\" in allow_methods:\n             allow_methods = ALL_METHODS\n \n+        if allow_origin_regex is not None:\n+            regex = re.compile(allow_origin_regex)\n+            allow_origin_regex = regex\n+\n         simple_headers = {}\n         if \"*\" in allow_origins:\n             simple_headers[\"Access-Control-Allow-Origin\"] = \"*\"\n@@ -53,6 +59,7 @@\n         self.allow_headers = allow_headers\n         self.allow_all_origins = \"*\" in allow_origins\n         self.allow_all_headers = \"*\" in allow_headers\n+        self.allow_origin_regex = allow_origin_regex\n         self.simple_headers = simple_headers\n         self.preflight_headers = preflight_headers\n \n@@ -66,12 +73,22 @@\n                 if method == \"OPTIONS\" and \"access-control-request-method\" in headers:\n                     return self.preflight_response(request_headers=headers)\n                 else:\n-                    return functools.partial(\n-                        self.simple_response, scope=scope, origin=origin\n-                    )\n+                    if self.is_allowed_origin(origin=origin):\n+                        return functools.partial(\n+                            self.simple_response, scope=scope, origin=origin\n+                        )\n+                    return PlainTextResponse(\"Disallowed CORS origin\", status_code=400)\n \n         return self.app(scope)\n \n+    def is_allowed_origin(self, origin):\n+        if self.allow_origin_regex:\n+            return self.allow_origin_regex.match(origin)\n+        if self.allow_all_origins:\n+            return True\n+\n+        return origin in self.allow_origins\n+\n     def preflight_response(self, request_headers):\n         requested_origin = request_headers[\"origin\"]\n         requested_method = request_headers[\"access-control-request-method\"]\n@@ -84,7 +101,7 @@\n         # If we only allow specific origins, then we have to mirror back\n         # the Origin header in the response.\n         if not self.allow_all_origins:\n-            if requested_origin in self.allow_origins:\n+            if self.is_allowed_origin(origin=requested_origin):\n                 headers[\"Access-Control-Allow-Origin\"] = requested_origin\n             else:\n                 failures.append(\"origin\")\n@@ -125,7 +142,7 @@\n \n         # If we only allow specific origins, then we have to mirror back\n         # the Origin header in the response.\n-        if not self.allow_all_origins and origin in self.allow_origins:\n+        if not self.allow_all_origins and self.is_allowed_origin(origin=origin):\n             headers[\"Access-Control-Allow-Origin\"] = origin\n         headers.update(self.simple_headers)\n         await send(message)\n", "issue": "Add `allow_origin_regex` to CORSMiddleware.\nIt'd be helpful if `CORSMiddleware` supported an `allow_origin_regex`, so that users could do...\r\n\r\n```python\r\n# Enforce a subdomain CORS policy\r\napp.add_middleware(CORSMiddleware, allow_origin_regex=\"(http|https)://*.example.com\")\r\n```\r\n\r\nOr...\r\n\r\n```python\r\n# Enforce an HTTPS-only CORS policy.\r\napp.add_middleware(CORSMiddleware, allow_origin_regex=\"https://*\")\r\n```\r\n\r\nThe string should be compiled to a regex by the middleware and matches should be anchored to the start/end of the origin string.\n", "before_files": [{"content": "from starlette.datastructures import Headers, MutableHeaders, URL\nfrom starlette.responses import PlainTextResponse\nfrom starlette.types import ASGIApp, ASGIInstance, Scope\nimport functools\nimport typing\n\n\nALL_METHODS = (\"DELETE\", \"GET\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\")\n\n\nclass CORSMiddleware:\n    def __init__(\n        self,\n        app: ASGIApp,\n        allow_origins: typing.Sequence[str] = (),\n        allow_methods: typing.Sequence[str] = (\"GET\",),\n        allow_headers: typing.Sequence[str] = (),\n        allow_credentials: bool = False,\n        expose_headers: typing.Sequence[str] = (),\n        max_age: int = 600,\n    ):\n\n        if \"*\" in allow_methods:\n            allow_methods = ALL_METHODS\n\n        simple_headers = {}\n        if \"*\" in allow_origins:\n            simple_headers[\"Access-Control-Allow-Origin\"] = \"*\"\n        if allow_credentials:\n            simple_headers[\"Access-Control-Allow-Credentials\"] = \"true\"\n        if expose_headers:\n            simple_headers[\"Access-Control-Expose-Headers\"] = \", \".join(expose_headers)\n\n        preflight_headers = {}\n        if \"*\" in allow_origins:\n            preflight_headers[\"Access-Control-Allow-Origin\"] = \"*\"\n        else:\n            preflight_headers[\"Vary\"] = \"Origin\"\n        preflight_headers.update(\n            {\n                \"Access-Control-Allow-Methods\": \", \".join(allow_methods),\n                \"Access-Control-Max-Age\": str(max_age),\n            }\n        )\n        if allow_headers and \"*\" not in allow_headers:\n            preflight_headers[\"Access-Control-Allow-Headers\"] = \", \".join(allow_headers)\n        if allow_credentials:\n            preflight_headers[\"Access-Control-Allow-Credentials\"] = \"true\"\n\n        self.app = app\n        self.allow_origins = allow_origins\n        self.allow_methods = allow_methods\n        self.allow_headers = allow_headers\n        self.allow_all_origins = \"*\" in allow_origins\n        self.allow_all_headers = \"*\" in allow_headers\n        self.simple_headers = simple_headers\n        self.preflight_headers = preflight_headers\n\n    def __call__(self, scope: Scope):\n        if scope[\"type\"] == \"http\":\n            method = scope[\"method\"]\n            headers = Headers(scope[\"headers\"])\n            origin = headers.get(\"origin\")\n\n            if origin is not None:\n                if method == \"OPTIONS\" and \"access-control-request-method\" in headers:\n                    return self.preflight_response(request_headers=headers)\n                else:\n                    return functools.partial(\n                        self.simple_response, scope=scope, origin=origin\n                    )\n\n        return self.app(scope)\n\n    def preflight_response(self, request_headers):\n        requested_origin = request_headers[\"origin\"]\n        requested_method = request_headers[\"access-control-request-method\"]\n        requested_headers = request_headers.get(\"access-control-request-headers\")\n        requested_cookie = \"cookie\" in request_headers\n\n        headers = dict(self.preflight_headers)\n        failures = []\n\n        # If we only allow specific origins, then we have to mirror back\n        # the Origin header in the response.\n        if not self.allow_all_origins:\n            if requested_origin in self.allow_origins:\n                headers[\"Access-Control-Allow-Origin\"] = requested_origin\n            else:\n                failures.append(\"origin\")\n\n        if requested_method not in self.allow_methods:\n            failures.append(\"method\")\n\n        # If we allow all headers, then we have to mirror back any requested\n        # headers in the response.\n        if self.allow_all_headers and requested_headers is not None:\n            headers[\"Access-Control-Allow-Headers\"] = requested_headers\n        elif requested_headers is not None:\n            for header in requested_headers.split(\",\"):\n                if header.strip() not in self.allow_headers:\n                    failures.append(\"headers\")\n\n        # We don't strictly need to use 400 responses here, since its up to\n        # the browser to enforce the CORS policy, but its more informative\n        # if we do.\n        if failures:\n            failure_text = \"Disallowed CORS \" + \", \".join(failures)\n            return PlainTextResponse(failure_text, status_code=400, headers=headers)\n\n        return PlainTextResponse(\"OK\", status_code=200, headers=headers)\n\n    async def simple_response(self, receive, send, scope=None, origin=None):\n        inner = self.app(scope)\n        send = functools.partial(self.send, send=send, origin=origin)\n        await inner(receive, send)\n\n    async def send(self, message, send=None, origin=None):\n        if message[\"type\"] != \"http.response.start\":\n            await send(message)\n            return\n\n        message.setdefault(\"headers\", [])\n        headers = MutableHeaders(message[\"headers\"])\n\n        # If we only allow specific origins, then we have to mirror back\n        # the Origin header in the response.\n        if not self.allow_all_origins and origin in self.allow_origins:\n            headers[\"Access-Control-Allow-Origin\"] = origin\n        headers.update(self.simple_headers)\n        await send(message)\n", "path": "starlette/middleware/cors.py"}], "after_files": [{"content": "from starlette.datastructures import Headers, MutableHeaders, URL\nfrom starlette.responses import PlainTextResponse\nfrom starlette.types import ASGIApp, ASGIInstance, Scope\nimport functools\nimport typing\nimport re\n\n\nALL_METHODS = (\"DELETE\", \"GET\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\")\n\n\nclass CORSMiddleware:\n    def __init__(\n        self,\n        app: ASGIApp,\n        allow_origins: typing.Sequence[str] = (),\n        allow_methods: typing.Sequence[str] = (\"GET\",),\n        allow_headers: typing.Sequence[str] = (),\n        allow_credentials: bool = False,\n        allow_origin_regex: str = None,\n        expose_headers: typing.Sequence[str] = (),\n        max_age: int = 600,\n    ):\n\n        if \"*\" in allow_methods:\n            allow_methods = ALL_METHODS\n\n        if allow_origin_regex is not None:\n            regex = re.compile(allow_origin_regex)\n            allow_origin_regex = regex\n\n        simple_headers = {}\n        if \"*\" in allow_origins:\n            simple_headers[\"Access-Control-Allow-Origin\"] = \"*\"\n        if allow_credentials:\n            simple_headers[\"Access-Control-Allow-Credentials\"] = \"true\"\n        if expose_headers:\n            simple_headers[\"Access-Control-Expose-Headers\"] = \", \".join(expose_headers)\n\n        preflight_headers = {}\n        if \"*\" in allow_origins:\n            preflight_headers[\"Access-Control-Allow-Origin\"] = \"*\"\n        else:\n            preflight_headers[\"Vary\"] = \"Origin\"\n        preflight_headers.update(\n            {\n                \"Access-Control-Allow-Methods\": \", \".join(allow_methods),\n                \"Access-Control-Max-Age\": str(max_age),\n            }\n        )\n        if allow_headers and \"*\" not in allow_headers:\n            preflight_headers[\"Access-Control-Allow-Headers\"] = \", \".join(allow_headers)\n        if allow_credentials:\n            preflight_headers[\"Access-Control-Allow-Credentials\"] = \"true\"\n\n        self.app = app\n        self.allow_origins = allow_origins\n        self.allow_methods = allow_methods\n        self.allow_headers = allow_headers\n        self.allow_all_origins = \"*\" in allow_origins\n        self.allow_all_headers = \"*\" in allow_headers\n        self.allow_origin_regex = allow_origin_regex\n        self.simple_headers = simple_headers\n        self.preflight_headers = preflight_headers\n\n    def __call__(self, scope: Scope):\n        if scope[\"type\"] == \"http\":\n            method = scope[\"method\"]\n            headers = Headers(scope[\"headers\"])\n            origin = headers.get(\"origin\")\n\n            if origin is not None:\n                if method == \"OPTIONS\" and \"access-control-request-method\" in headers:\n                    return self.preflight_response(request_headers=headers)\n                else:\n                    if self.is_allowed_origin(origin=origin):\n                        return functools.partial(\n                            self.simple_response, scope=scope, origin=origin\n                        )\n                    return PlainTextResponse(\"Disallowed CORS origin\", status_code=400)\n\n        return self.app(scope)\n\n    def is_allowed_origin(self, origin):\n        if self.allow_origin_regex:\n            return self.allow_origin_regex.match(origin)\n        if self.allow_all_origins:\n            return True\n\n        return origin in self.allow_origins\n\n    def preflight_response(self, request_headers):\n        requested_origin = request_headers[\"origin\"]\n        requested_method = request_headers[\"access-control-request-method\"]\n        requested_headers = request_headers.get(\"access-control-request-headers\")\n        requested_cookie = \"cookie\" in request_headers\n\n        headers = dict(self.preflight_headers)\n        failures = []\n\n        # If we only allow specific origins, then we have to mirror back\n        # the Origin header in the response.\n        if not self.allow_all_origins:\n            if self.is_allowed_origin(origin=requested_origin):\n                headers[\"Access-Control-Allow-Origin\"] = requested_origin\n            else:\n                failures.append(\"origin\")\n\n        if requested_method not in self.allow_methods:\n            failures.append(\"method\")\n\n        # If we allow all headers, then we have to mirror back any requested\n        # headers in the response.\n        if self.allow_all_headers and requested_headers is not None:\n            headers[\"Access-Control-Allow-Headers\"] = requested_headers\n        elif requested_headers is not None:\n            for header in requested_headers.split(\",\"):\n                if header.strip() not in self.allow_headers:\n                    failures.append(\"headers\")\n\n        # We don't strictly need to use 400 responses here, since its up to\n        # the browser to enforce the CORS policy, but its more informative\n        # if we do.\n        if failures:\n            failure_text = \"Disallowed CORS \" + \", \".join(failures)\n            return PlainTextResponse(failure_text, status_code=400, headers=headers)\n\n        return PlainTextResponse(\"OK\", status_code=200, headers=headers)\n\n    async def simple_response(self, receive, send, scope=None, origin=None):\n        inner = self.app(scope)\n        send = functools.partial(self.send, send=send, origin=origin)\n        await inner(receive, send)\n\n    async def send(self, message, send=None, origin=None):\n        if message[\"type\"] != \"http.response.start\":\n            await send(message)\n            return\n\n        message.setdefault(\"headers\", [])\n        headers = MutableHeaders(message[\"headers\"])\n\n        # If we only allow specific origins, then we have to mirror back\n        # the Origin header in the response.\n        if not self.allow_all_origins and self.is_allowed_origin(origin=origin):\n            headers[\"Access-Control-Allow-Origin\"] = origin\n        headers.update(self.simple_headers)\n        await send(message)\n", "path": "starlette/middleware/cors.py"}]}