Repository: litestar-org/litestar

Issue:
Bug: session middleware cookies always include segment number
### Description

The docstring of [litestar.middleware.session.client_side.CookieBackendConfig](https://github.com/litestar-org/litestar/blob/04a9501d719c26ff4ef8451d216707a1b72a1c77/litestar/middleware/session/client_side.py#L209) suggests that key for the cookie inside the header is of the form `session=<data>`, unless the cookie is over 4KB in which case it's split into segments named e.g. `session-0=<data-0>`, etc.  

However, even when the cookie is under 4KB, the segment number is present in the key, i.e. it's impossible to specify that the key be named `session` -- it will always turn into `session-0`.  The reason for this is that 
 `litestar.middleware.session.client_side.ClientSideSessionBackend._create_session_cookies` returns the result of the list comprehension which includes `[Cookie(..., key=f"{self.config.key}-{i}", ...) for i, datum in enumerate(data)]`.

### URL to code causing the issue

_No response_

### MCVE

_No response_

### Steps to reproduce

_No response_

### Screenshots

_No response_

### Logs

_No response_

### Litestar Version

2.6.0

### Platform

- [X] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)

<!-- POLAR PLEDGE BADGE START -->
---
> [!NOTE]  
> While we are open for sponsoring on [GitHub Sponsors](https://github.com/sponsors/litestar-org/) and 
> [OpenCollective](https://opencollective.com/litestar), we also utilize [Polar.sh](https://polar.sh/) to engage in pledge-based sponsorship.
>
> Check out all issues funded or available for funding [on our Polar.sh dashboard](https://polar.sh/litestar-org)
> * If you would like to see an issue prioritized, make a pledge towards it!
> * We receive the pledge once the issue is completed & verified
> * This, along with engagement in the community, helps us know which features are a priority to our users.

<a href="https://polar.sh/litestar-org/litestar/issues/3090">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/litestar-org/litestar/issues/3090/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/litestar-org/litestar/issues/3090/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->



Discussion:
@betaprior since you seem to have figured out the issue, would you like to raise a PR with a fix?

Resolving patch:
diff --git a/litestar/middleware/session/client_side.py b/litestar/middleware/session/client_side.py
--- a/litestar/middleware/session/client_side.py
+++ b/litestar/middleware/session/client_side.py
@@ -108,7 +108,10 @@ def get_cookie_keys(self, connection: ASGIConnection) -> list[str]:
         return sorted(key for key in connection.cookies if self.cookie_re.fullmatch(key))
 
     def _create_session_cookies(self, data: list[bytes], cookie_params: dict[str, Any] | None = None) -> list[Cookie]:
-        """Create a list of cookies containing the session data."""
+        """Create a list of cookies containing the session data.
+        If the data is split into multiple cookies, the key will be of the format ``session-{segment number}``,
+        however if only one cookie is needed, the key will be ``session``.
+        """
         if cookie_params is None:
             cookie_params = dict(
                 extract_dataclass_items(
@@ -117,6 +120,16 @@ def _create_session_cookies(self, data: list[bytes], cookie_params: dict[str, An
                     include={f for f in Cookie.__dict__ if f not in ("key", "secret")},
                 )
             )
+
+        if len(data) == 1:
+            return [
+                Cookie(
+                    value=data[0].decode("utf-8"),
+                    key=self.config.key,
+                    **cookie_params,
+                )
+            ]
+
         return [
             Cookie(
                 value=datum.decode("utf-8"),


Test patch:
diff --git a/tests/unit/test_middleware/test_session/test_client_side_backend.py b/tests/unit/test_middleware/test_session/test_client_side_backend.py
--- a/tests/unit/test_middleware/test_session/test_client_side_backend.py
+++ b/tests/unit/test_middleware/test_session/test_client_side_backend.py
@@ -113,16 +113,28 @@ def handler(request: Request) -> None:
         # Then you only need to check if number of cookies set are more than the multiplying number.
         request.session.update(create_session(size=CHUNK_SIZE * chunks_multiplier))
 
+    @get(path="/test_short_cookie")
+    def handler_short_cookie(request: Request) -> None:
+        # Check the naming of a cookie that's short enough to not get broken into chunks
+        request.session.update(create_session())
+
     with create_test_client(
         route_handlers=[handler],
         middleware=[cookie_session_backend_config.middleware],
     ) as client:
         response = client.get("/test")
 
-    assert len(response.cookies) > chunks_multiplier
-    # If it works for the multiple chunks of session, it works for the single chunk too. So, just check if "session-0"
-    # exists.
-    assert "session-0" in response.cookies
+        assert len(response.cookies) > chunks_multiplier
+        assert "session-0" in response.cookies
+
+    with create_test_client(
+        route_handlers=[handler_short_cookie],
+        middleware=[cookie_session_backend_config.middleware],
+    ) as client:
+        response = client.get("/test_short_cookie")
+
+        assert len(response.cookies) == 1
+        assert "session" in response.cookies
 
 
 def test_session_cookie_name_matching(cookie_session_backend_config: "CookieBackendConfig") -> None: