Repository: qdrant/qdrant (language: rust)

Issue:
Handle double slashes in API requests correctly
qdrant_client api request -> [https://058d11b4-02ae-4424-be5a-asadgjh7ab9158c.eastus-0.azure.cloud.qdrant.io:6333//collections](https://058d11b4-02ae-4424-be5a-asadgjh7ab9158c.eastus-0.azure.cloud.qdrant.io:6333//collections) (masked host)
Current behaviour : {"error":"bad request"}
Expected behaviour: It should handles properly such that caller of APIs not faces unexpected issue `{"error":"bad request"}` and it should return responses.


Discussion / hints:
Server-Side URL Normalization
The Qdrant server should also normalize incoming URLs to ensure robust handling of improperly formatted requests. This can include:

Automatically collapsing multiple consecutive slashes (//) into a single slash (/).
Why not just format properly?
> Why not just format properly?
yes , we can but the issue of trailing slashes in the Qdrant host URL when initializing the client can lead to subtle bugs or unexpected behavior. To handle this effectively and improve the developer experience, it's best to address the problem at both ends—on the server side and on the client side.

1. Server-Side Handling
On the server side, Qdrant can be configured to gracefully handle URLs with or without trailing slashes. This approach ensures the API is robust and avoids breaking clients accidentally.

2. Client-Side Handling 
On the client side, Qdrant SDKs or user-written clients should sanitize the host URL to avoid issues caused by trailing slashes.

(currently we as user of qdrant, handled our side, due to unexpected behaviour it slow down our development.

Fix patch:
diff --git a/src/actix/mod.rs b/src/actix/mod.rs
index 524935c4751..3209f030aba 100644
--- a/src/actix/mod.rs
+++ b/src/actix/mod.rs
@@ -12,7 +12,7 @@ use ::api::rest::models::{ApiResponse, ApiStatus, VersionInfo};
 use actix_cors::Cors;
 use actix_multipart::form::tempfile::TempFileConfig;
 use actix_multipart::form::MultipartFormConfig;
-use actix_web::middleware::{Compress, Condition, Logger};
+use actix_web::middleware::{Compress, Condition, Logger, NormalizePath};
 use actix_web::{error, get, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
 use actix_web_extras::middleware::Condition as ConditionEx;
 use api::facet_api::config_facet_api;
@@ -115,6 +115,8 @@ pub fn init(
                 .wrap(ConditionEx::from_option(auth_keys.as_ref().map(
                     |auth_keys| Auth::new(auth_keys.clone(), api_key_whitelist.clone()),
                 )))
+                // Normalize path
+                .wrap(NormalizePath::trim())
                 .wrap(Condition::new(settings.service.enable_cors, cors))
                 .wrap(
                     // Set up logger, but avoid logging hot status endpoints


Test patch:
diff --git a/tests/basic_api_test.sh b/tests/basic_api_test.sh
index 9527d96182b..c6905c4cfc5 100755
--- a/tests/basic_api_test.sh
+++ b/tests/basic_api_test.sh
@@ -146,3 +146,22 @@ curl -L -X POST "http://$QDRANT_HOST/collections/test_collection/points/search"
       "vector": [0.2, 0.1, 0.9, 0.7],
       "top": 3
   }' | jq
+
+# test double forward slash handling
+curl -L -X POST "http://$QDRANT_HOST//collections/test_collection/points/search/" \
+  -H 'Content-Type: application/json' "${qdrant_host_headers[@]}" \
+  --fail -s \
+  --data-raw '{
+      "filter": {
+          "should": [
+              {
+                  "key": "city", 
+                  "match": {
+                      "value": "London"
+                  }
+              }
+          ]
+      },
+      "vector": [0.2, 0.1, 0.9, 0.7],
+      "top": 3
+  }' | jq