From 65f3bb9ae256344d269d847a2d5c41b8ae11075f Mon Sep 17 00:00:00 2001 From: Anton Troynikov Date: Tue, 21 Nov 2023 15:32:18 -0800 Subject: [PATCH] [ENH] Verify clients use HTTP 1.1 + (#1416) ## Description of changes This change adds FastAPI server-side verification that clients are using HTTP 1.1 or 2, and adds an appropriate `ChromaError` called `InvalidHTTPVersion` if they're not. The `requests` library used by the Chroma python client uses 1.1 by default so no changes are needed. ## Test plan Tested manually by setting https://github.com/chroma-core/chroma/blob/2e6602072a3191407a2aff189e398ba73ba952fc/chromadb/server/fastapi/__init__.py#L95 to instead be ```python http_version not in ["1"] ``` And observed that attempting to connect raises the appropriate exception, propagated from the server: ``` Traceback (most recent call last): File "/Users/antontroynikov/Projects/chroma/chromadb/api/client.py", line 427, in _validate_tenant_database self._admin_client.get_tenant(name=tenant) File "/Users/antontroynikov/Projects/chroma/chromadb/api/client.py", line 472, in get_tenant return self._server.get_tenant(name=name) File "/Users/antontroynikov/Projects/chroma/chromadb/telemetry/opentelemetry/__init__.py", line 127, in wrapper return f(*args, **kwargs) File "/Users/antontroynikov/Projects/chroma/chromadb/api/fastapi.py", line 198, in get_tenant raise_chroma_error(resp) File "/Users/antontroynikov/Projects/chroma/chromadb/api/fastapi.py", line 625, in raise_chroma_error raise chroma_error chromadb.errors.InvalidHTTPVersion: HTTP version 1.1 is not supported ``` The same test with the Javascript client returns a `400` error as expected. ## Documentation Changes N/A ## TODO - [x] Check Javascript client --- chromadb/errors.py | 8 ++++++++ chromadb/server/fastapi/__init__.py | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/chromadb/errors.py b/chromadb/errors.py index 9b53d8fec4a..89320d44525 100644 --- a/chromadb/errors.py +++ b/chromadb/errors.py @@ -57,10 +57,18 @@ def name(cls) -> str: return "InvalidUUID" +class InvalidHTTPVersion(ChromaError): + @classmethod + @overrides + def name(cls) -> str: + return "InvalidHTTPVersion" + + error_types: Dict[str, Type[ChromaError]] = { "InvalidDimension": InvalidDimensionException, "InvalidCollection": InvalidCollectionException, "IDAlreadyExists": IDAlreadyExistsError, "DuplicateID": DuplicateIDError, "InvalidUUID": InvalidUUIDError, + "InvalidHTTPVersion": InvalidHTTPVersion, } diff --git a/chromadb/server/fastapi/__init__.py b/chromadb/server/fastapi/__init__.py index 376e2c81225..446415731ec 100644 --- a/chromadb/server/fastapi/__init__.py +++ b/chromadb/server/fastapi/__init__.py @@ -35,6 +35,7 @@ ChromaError, InvalidUUIDError, InvalidDimensionException, + InvalidHTTPVersion, ) from chromadb.server.fastapi.types import ( AddEmbedding, @@ -87,6 +88,15 @@ async def catch_exceptions_middleware( return JSONResponse(content={"error": repr(e)}, status_code=500) +async def check_http_version_middleware( + request: Request, call_next: Callable[[Request], Any] +) -> Response: + http_version = request.scope.get("http_version") + if http_version not in ["1.1", "2"]: + raise InvalidHTTPVersion(f"HTTP version {http_version} is not supported") + return await call_next(request) + + def _uuid(uuid_str: str) -> UUID: try: return UUID(uuid_str) @@ -131,6 +141,7 @@ def __init__(self, settings: Settings): self._opentelemetry_client = self._api.require(OpenTelemetryClient) self._system.start() + self._app.middleware("http")(check_http_version_middleware) self._app.middleware("http")(catch_exceptions_middleware) self._app.add_middleware( CORSMiddleware,