internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: encode/starlette

Issue:
[bug] Invalid cookie name leads to exception
When handling a request with an invalid cookie name (does not conform to RFC2109) starlette raises an exception. i.e iam/cookiename
This is because Starlette uses Python's stdlib cookie library, which is very strict.
I do understand the strictness, but in real life scenarios you receive such malformed cookies and I want to handle those requests.
My suggestion for a solution would be to catch those exceptions and ignore the invalid cookie. 
** EDIT **
I just realized stdlib is used for the whole cookie header, hence can't ignore only one cookie.
I'll create a PR for ignoring the whole cookie on such case, but maybe we should create our own Cookie/Morsel class and override the methods to ignore such error in the inbound case?


Discussion:

Resolving patch:
diff --git a/starlette/requests.py b/starlette/requests.py
--- a/starlette/requests.py
+++ b/starlette/requests.py
@@ -91,7 +91,10 @@ def cookies(self) -> typing.Dict[str, str]:
             cookie_header = self.headers.get("cookie")
             if cookie_header:
                 cookie = http.cookies.SimpleCookie()  # type: http.cookies.BaseCookie
-                cookie.load(cookie_header)
+                try:
+                    cookie.load(cookie_header)
+                except http.cookies.CookieError:
+                    pass
                 for key, morsel in cookie.items():
                     cookies[key] = morsel.value
             self._cookies = cookies


Test patch:
diff --git a/tests/test_requests.py b/tests/test_requests.py
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -285,6 +285,17 @@ async def app(scope, receive, send):
     assert response.text == "Hello, cookies!"
 
 
+def test_invalid_cookie():
+    async def app(scope, receive, send):
+        request = Request(scope, receive)
+        response = JSONResponse({"cookies": request.cookies})
+        await response(scope, receive, send)
+
+    client = TestClient(app)
+    response = client.get("/", cookies={"invalid/cookie": "test", "valid": "test2"})
+    assert response.json() == {"cookies": {}}
+
+
 def test_chunked_encoding():
     async def app(scope, receive, send):
         request = Request(scope, receive)
Repository: encode/starlette

Issue:
RedirectResponse does not accept background argument
The [RedirectResponse](https://github.com/encode/starlette/blob/e57fa2a6b1b19dd118c26a21b2f5bd4dbe46c64a/starlette/responses.py#L174) class does not accept the `background` parameter.

My use case is I am submitting a form and then doing an `HTTP 303 See Other` ([ref](https://httpstatuses.com/303)), redirecting the user to a page specific to the output of the form. That page allows the user to download the processed data, once the background task completes it.

🤔 Perhaps this is intentional?


Discussion:
No it's not intentional. PRs welcome.

Resolving patch:
diff --git a/starlette/responses.py b/starlette/responses.py
--- a/starlette/responses.py
+++ b/starlette/responses.py
@@ -173,9 +173,15 @@ def render(self, content: typing.Any) -> bytes:
 
 class RedirectResponse(Response):
     def __init__(
-        self, url: typing.Union[str, URL], status_code: int = 307, headers: dict = None
+        self,
+        url: typing.Union[str, URL],
+        status_code: int = 307,
+        headers: dict = None,
+        background: BackgroundTask = None,
     ) -> None:
-        super().__init__(content=b"", status_code=status_code, headers=headers)
+        super().__init__(
+            content=b"", status_code=status_code, headers=headers, background=background
+        )
         self.headers["location"] = quote_plus(str(url), safe=":/%#?&=@[]!$&'()*+,;")
 
 


Test patch:
Repository: encode/starlette

Issue:
Arbitrary path traversal possible with StaticFiles
If you pass in an absolute path by using an URL formatted like this: http://localhost:5000/staticmount/c%3a%5cwindows%5cwin.ini

Then you can read any file on the host machine.

This is because of the following python behavior:

```python
>>> dir = "c:\workspace\somepoject\somepackag\statics"
>>> path = "c:\windows\win.ini"
>>> os.path.join(dir, path)
'c:\\windows\\win.ini'
```

So staticfiles.py needs to be updated by placing in a check for absolute paths inside lookup_path(...).


Discussion:
Thanks. We're checking and handling path breakouts, but we've obviously not covered the *windows-specific* case of breaking out using a drive-absolute path. Will have a look into resolving this as a priority.

Resolving patch:
diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py
--- a/starlette/staticfiles.py
+++ b/starlette/staticfiles.py
@@ -107,12 +107,6 @@ async def get_response(self, path: str, scope: Scope) -> Response:
         if scope["method"] not in ("GET", "HEAD"):
             return PlainTextResponse("Method Not Allowed", status_code=405)
 
-        if path.startswith(".."):
-            # Most clients will normalize the path, so we shouldn't normally
-            # get this, but don't allow misbehaving clients to break out of
-            # the static files directory.
-            return PlainTextResponse("Not Found", status_code=404)
-
         full_path, stat_result = await self.lookup_path(path)
 
         if stat_result and stat.S_ISREG(stat_result.st_mode):
@@ -147,6 +141,12 @@ async def lookup_path(
     ) -> typing.Tuple[str, typing.Optional[os.stat_result]]:
         for directory in self.all_directories:
             full_path = os.path.join(directory, path)
+            if (
+                os.path.commonprefix([os.path.realpath(full_path), directory])
+                != directory
+            ):
+                # Don't allow misbehaving clients to break out of the static files directory.
+                continue
             try:
                 stat_result = await aio_stat(full_path)
                 return (full_path, stat_result)


Test patch:
Repository: encode/starlette

Issue:
Asynchronous classmethod handlers not recognized as coroutines if wrapped in functools.partial
https://github.com/encode/starlette/blob/e4307065ea6dbe708fba9643d14fe7adfff06d46/starlette/routing.py#L38

Although the recent 0.14.0 version adds support for functools.partial for Python 3.6 and Python 3.7, that support appears to be incomplete, not working for classmethods in Python 3.8.

I was unaware of the fact that `inspect.iscoroutinefunction` does not properly detect a functools.partial as async if it wraps an async classmethod.

TLDR: the previous PR was too cautious, we can always unwrap the functools.partial object to be sure we check the underlying object.


Discussion:

Resolving patch:
diff --git a/starlette/routing.py b/starlette/routing.py
--- a/starlette/routing.py
+++ b/starlette/routing.py
@@ -2,7 +2,6 @@
 import functools
 import inspect
 import re
-import sys
 import traceback
 import typing
 from enum import Enum
@@ -33,11 +32,10 @@ class Match(Enum):
 def iscoroutinefunction_or_partial(obj: typing.Any) -> bool:
     """
     Correctly determines if an object is a coroutine function,
-    with a fix for partials on Python < 3.8.
+    including those wrapped in functools.partial objects.
     """
-    if sys.version_info < (3, 8):  # pragma: no cover
-        while isinstance(obj, functools.partial):
-            obj = obj.func
+    while isinstance(obj, functools.partial):
+        obj = obj.func
     return inspect.iscoroutinefunction(obj)
 
 


Test patch:
diff --git a/tests/test_routing.py b/tests/test_routing.py
--- a/tests/test_routing.py
+++ b/tests/test_routing.py
@@ -590,16 +590,35 @@ def run_shutdown():
             pass  # pragma: nocover
 
 
+class AsyncEndpointClassMethod:
+    @classmethod
+    async def async_endpoint(cls, arg, request):
+        return JSONResponse({"arg": arg})
+
+
 async def _partial_async_endpoint(arg, request):
     return JSONResponse({"arg": arg})
 
 
 partial_async_endpoint = functools.partial(_partial_async_endpoint, "foo")
+partial_cls_async_endpoint = functools.partial(
+    AsyncEndpointClassMethod.async_endpoint, "foo"
+)
 
-partial_async_app = Router(routes=[Route("/", partial_async_endpoint)])
+partial_async_app = Router(
+    routes=[
+        Route("/", partial_async_endpoint),
+        Route("/cls", partial_cls_async_endpoint),
+    ]
+)
 
 
 def test_partial_async_endpoint():
-    response = TestClient(partial_async_app).get("/")
+    test_client = TestClient(partial_async_app)
+    response = test_client.get("/")
     assert response.status_code == 200
     assert response.json() == {"arg": "foo"}
+
+    cls_method_response = test_client.get("/cls")
+    assert cls_method_response.status_code == 200
+    assert cls_method_response.json() == {"arg": "foo"}
Repository: encode/starlette

Issue:
HTTPConnection::__get_item__ has incorrect type signature
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

### Describe the bug

`HTTPConnection`'s `__get_item__` has the return type `str`. However, it returns the value for the provided key in the scope. Not all values in the scope are strings, (See documentation here: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope

Since `WebSocket` inherits this method it also has the wrong return type.

### To reproduce
This example uses WebSocket because that's how I originally found the issue.

Run mypy
```bash
mypy --pretty main.py
```
On the following code
```python
from starlette.types import Scope, Receive, Send
from starlette.websockets import WebSocket


async def app(scope: Scope, receive: Receive, send: Send) -> None:
    websocket = WebSocket(scope=scope, receive=receive, send=send)
    await websocket.accept()
    ip, port = websocket['client']  # mypy fails to typecheck, because websocket['client'] typed as a str
    # Succeeds at runtime, because websocket['client'] is not a string
    assert isinstance(ip, str)
    assert isinstance(port, int)
```

### Expected behavior

mypy typecheck should pass for valid code

### Actual behavior

Mypy fails to typecheck with the following error:

```
main.py:8: error: Unpacking a string is disallowed
        ip, port = websocket['client']  # mypy fails to typecheck, because webso...
                   ^
```


### Environment

- OS: linux/windows/mac
- Python version: 3.9
- Starlette version: 0.14.1

### Additional context

Changing the return type of `__get_item__` to `Any` is likely the right thing to do here, which would be backwards-compatible for those currently using mypy. 

As an extra solution to this problem, it would be great to expose the correct types for the scope using a TypedDict (couldn't be done for `__getitem__`, but could be done for the scope attribute on `WebSocket` and `HTTPConnection`), although that would be a breaking change for MyPy users, and also complicate the middleware story.

I'd be happy to work on either/both of these solutions if there's a consensus on what the right direction is.


Discussion:

Resolving patch:
diff --git a/starlette/requests.py b/starlette/requests.py
--- a/starlette/requests.py
+++ b/starlette/requests.py
@@ -65,7 +65,7 @@ def __init__(self, scope: Scope, receive: Receive = None) -> None:
         assert scope["type"] in ("http", "websocket")
         self.scope = scope
 
-    def __getitem__(self, key: str) -> str:
+    def __getitem__(self, key: str) -> typing.Any:
         return self.scope[key]
 
     def __iter__(self) -> typing.Iterator[str]:


Test patch:
Repository: encode/starlette

Issue:
Session cookie should use root path
The session cookie currently uses '/'.
It should really use the ASGI root path instead, in case the application is submounted.


Discussion:

Resolving patch:
diff --git a/starlette/middleware/sessions.py b/starlette/middleware/sessions.py
--- a/starlette/middleware/sessions.py
+++ b/starlette/middleware/sessions.py
@@ -49,14 +49,16 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
 
         async def send_wrapper(message: Message) -> None:
             if message["type"] == "http.response.start":
+                path = scope.get("root_path", "") or "/"
                 if scope["session"]:
                     # We have session data to persist.
                     data = b64encode(json.dumps(scope["session"]).encode("utf-8"))
                     data = self.signer.sign(data)
                     headers = MutableHeaders(scope=message)
-                    header_value = "%s=%s; path=/; Max-Age=%d; %s" % (
+                    header_value = "%s=%s; path=%s; Max-Age=%d; %s" % (
                         self.session_cookie,
                         data.decode("utf-8"),
+                        path,
                         self.max_age,
                         self.security_flags,
                     )
@@ -66,7 +68,7 @@ async def send_wrapper(message: Message) -> None:
                     headers = MutableHeaders(scope=message)
                     header_value = "{}={}; {}".format(
                         self.session_cookie,
-                        "null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;",
+                        f"null; path={path}; expires=Thu, 01 Jan 1970 00:00:00 GMT;",
                         self.security_flags,
                     )
                     headers.append("Set-Cookie", header_value)


Test patch:
diff --git a/tests/middleware/test_session.py b/tests/middleware/test_session.py
--- a/tests/middleware/test_session.py
+++ b/tests/middleware/test_session.py
@@ -101,3 +101,15 @@ def test_secure_session():
 
     response = secure_client.get("/view_session")
     assert response.json() == {"session": {}}
+
+
+def test_session_cookie_subpath():
+    app = create_app()
+    second_app = create_app()
+    second_app.add_middleware(SessionMiddleware, secret_key="example")
+    app.mount("/second_app", second_app)
+    client = TestClient(app, base_url="http://testserver/second_app")
+    response = client.post("second_app/update_session", json={"some": "data"})
+    cookie = response.headers["set-cookie"]
+    cookie_path = re.search(r"; path=(\S+);", cookie).groups()[0]
+    assert cookie_path == "/second_app"
Repository: encode/starlette

Issue:
Consider making `starlette.templating.Jinja2Templates.get_env` "protected"
Hi,
Thank you very much for your project! We are using it intensively through FastAPI.

While I was exploring the code base and looking how to use `Jinja2Templates`, I noticed the method `get_env`. At first I thought it was a getter for the environment, but eventually realized it is only called in the constructor of the class.

It would make much more sense to me if the method was called `_get_env`. This would signal that it should not be used outside of the class (or at least that the caller needs to know what she is doing).

For example, if I want to add filters or globals to the environment, I should write:

```
templates = Jinja2Templates(...)
templates.env.globals[...] = ...
```

instead of:

```
templates = Jinja2Templates(...)
templates.get_env().globals[...] = ...
```

As of current implementation, the second snippet has no effect.

Thank you very much for considering this request!


Discussion:

Resolving patch:
diff --git a/starlette/templating.py b/starlette/templating.py
--- a/starlette/templating.py
+++ b/starlette/templating.py
@@ -56,9 +56,9 @@ class Jinja2Templates:
 
     def __init__(self, directory: str) -> None:
         assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates"
-        self.env = self.get_env(directory)
+        self.env = self._create_env(directory)
 
-    def get_env(self, directory: str) -> "jinja2.Environment":
+    def _create_env(self, directory: str) -> "jinja2.Environment":
         @pass_context
         def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
             request = context["request"]


Test patch:
Repository: encode/starlette

Issue:
ImmutableMultiDict returns a List[Any] instead of a List[str]
`starlette.datastructures.ImmutableMultiDict.getlist()` could return a list of List[UploadFile] in some circumstances.

### Code to reproduce
```python
@router.post("/upload")
async def create_training_set(request: Request) -> Any:

    form = await request.form()
    files = form.getlist('file')

    print(type(files))
    print(type(files[0]))

    print(type(form.getlist('label')))
    print(type(form.getlist('label')[0]))
```
output:
```
<class 'list'>
<class 'starlette.datastructures.UploadFile'>
<class 'list'>
<class 'str'>
```
The request was made from a flutter (mobile) lib, if needed, I can provide more details about the request.

This is compliant with the [doc](https://www.starlette.io/requests/#request-files), it's just a Typing issue I would say


Discussion:

Resolving patch:
diff --git a/starlette/datastructures.py b/starlette/datastructures.py
--- a/starlette/datastructures.py
+++ b/starlette/datastructures.py
@@ -266,7 +266,7 @@ def __init__(
         self._dict = {k: v for k, v in _items}
         self._list = _items
 
-    def getlist(self, key: typing.Any) -> typing.List[str]:
+    def getlist(self, key: typing.Any) -> typing.List[typing.Any]:
         return [item_value for item_key, item_value in self._list if item_key == key]
 
     def keys(self) -> typing.KeysView:


Test patch:
Repository: encode/starlette

Issue:
Cannot define a custom HEAD handler in HTTPEndpoint
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

### Describe the bug

I am unable to set a custom head method for a HTTPEndpoint

### How to reproduce

Python:
```python
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import Response
from starlette.endpoints import HTTPEndpoint

class TestHandler(HTTPEndpoint):
    async def head(self, request):
        print("Called head")
        return Response("head")

    async def get(self, request):
        print("Called get")
        return Response("get")

routes = [
    Route("/", TestHandler),
]

app = Starlette(routes=routes)
```
Then run uvicorn, and call `curl -XHEAD localhost:8000/` and `curl -XGET localhost:8000/`

You will see in uvicorn logs, both times the get handler gets called.
```
Called get
INFO:     127.0.0.1:53438 - "HEAD / HTTP/1.1" 200 OK
Called get
INFO:     127.0.0.1:53440 - "GET / HTTP/1.1" 200 OK
```

### Expected behavior

When calling the HEAD method, it should invoke the head() function

### Actual behavior

When calling the HEAD method, it invokes the get() function


### Environment

- OS: Linux
- Python version: Python 3.9.5
- Starlette version: 0.15.0

### Additional context

The issue is caused by the following code:
https://github.com/encode/starlette/blob/master/starlette/endpoints.py#L26

It should see if there is a head method, and if so, don't call the get method when head is requested

Eg. linked issues, or a description of what you were trying to achieve. -->



Discussion:

Resolving patch:
diff --git a/starlette/endpoints.py b/starlette/endpoints.py
--- a/starlette/endpoints.py
+++ b/starlette/endpoints.py
@@ -23,7 +23,12 @@ def __await__(self) -> typing.Generator:
 
     async def dispatch(self) -> None:
         request = Request(self.scope, receive=self.receive)
-        handler_name = "get" if request.method == "HEAD" else request.method.lower()
+        handler_name = (
+            "get"
+            if request.method == "HEAD" and not hasattr(self, "head")
+            else request.method.lower()
+        )
+
         handler = getattr(self, handler_name, self.method_not_allowed)
         is_async = asyncio.iscoroutinefunction(handler)
         if is_async:


Test patch:
Repository: encode/starlette

Issue:
templateing: jinja2: pass kwargs for environment
I think it would be good to pass something like `env_kwargs` via https://github.com/blueyed/starlette/blob/24c135de71ac56a73f7f797258115941579155bf/starlette/templating.py#L51-L53.

While you can change the env afterwards, it would allow Jinja2 to validate e.g. `enable_async`, and call `load_extensions` etc.


Discussion:
Yup I'm up for this. We'll want to `autoescape` and `loader` be overridable there too.

Related to this is configuring `env.globals`.

We *could* just use `**options`, and support both `globals` and any valid Environment keyword arguments?
I think it should just allow more easily to set the env altogether.

`url_for` could be still set to the globals then by default.

But it is also the only reason for requiring `request` to be in the context (`ValueError('context must include a "request" key')`), and if `url_for` is not used in a template, it is not used (by default).  I.e. it might be better to raise the error then from `url_for` only.

Not sure about backward compatibility with regard to removing `directory` from the constructor, but #473 might indicate that it is not used much (at least with tested code).. ;)

I am using a quite customized version already myself, and that is fine, but wanted to provide this feedback anyway.
+1 as well, since as currently stands it's not possible to change common fields such as `auto_reload`, `enable_async` when loading the environment, especially when deploying through a docker environment. 

Resolving patch:
diff --git a/starlette/templating.py b/starlette/templating.py
--- a/starlette/templating.py
+++ b/starlette/templating.py
@@ -55,12 +55,14 @@ class Jinja2Templates:
     return templates.TemplateResponse("index.html", {"request": request})
     """
 
-    def __init__(self, directory: typing.Union[str, PathLike]) -> None:
+    def __init__(
+        self, directory: typing.Union[str, PathLike], **env_options: typing.Any
+    ) -> None:
         assert jinja2 is not None, "jinja2 must be installed to use Jinja2Templates"
-        self.env = self._create_env(directory)
+        self.env = self._create_env(directory, **env_options)
 
     def _create_env(
-        self, directory: typing.Union[str, PathLike]
+        self, directory: typing.Union[str, PathLike], **env_options: typing.Any
     ) -> "jinja2.Environment":
         @pass_context
         def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
@@ -68,7 +70,10 @@ def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
             return request.url_for(name, **path_params)
 
         loader = jinja2.FileSystemLoader(directory)
-        env = jinja2.Environment(loader=loader, autoescape=True)
+        env_options.setdefault("loader", loader)
+        env_options.setdefault("autoescape", True)
+
+        env = jinja2.Environment(**env_options)
         env.globals["url_for"] = url_for
         return env
 


Test patch: