66import logging
77import urllib .parse
88from typing import Any , Final , NamedTuple
9- from urllib .parse import quote
9+ from urllib .parse import quote , unquote
1010
1111from aiohttp import ClientTimeout , web
1212from models_library .api_schemas_storage import (
1818from models_library .projects_nodes_io import LocationID
1919from models_library .utils .fastapi_encoders import jsonable_encoder
2020from pydantic import AnyUrl , BaseModel , ByteSize , TypeAdapter
21+ from servicelib .aiohttp import status
2122from servicelib .aiohttp .client_session import get_client_session
2223from servicelib .aiohttp .requests_validation import (
2324 parse_request_body_as ,
@@ -61,7 +62,13 @@ def _to_storage_url(request: web.Request) -> URL:
6162 # strip basepath from webserver API path (i.e. webserver api version)
6263 # >>> URL('http://storage:1234/v5/storage/asdf/').raw_parts[3:]
6364 suffix = "/" .join (request .url .parts [basepath_index :])
64- fastapi_encoded_suffix = urllib .parse .quote (suffix , safe = "/" )
65+ # we need to quote anything before the column, but not the column
66+ if (column_index := suffix .find (":" )) > 0 :
67+ fastapi_encoded_suffix = (
68+ urllib .parse .quote (suffix [:column_index ], safe = "/" ) + suffix [column_index :]
69+ )
70+ else :
71+ fastapi_encoded_suffix = urllib .parse .quote (suffix , safe = "/" )
6572
6673 return (
6774 url .joinpath (fastapi_encoded_suffix , encoded = True )
@@ -82,9 +89,10 @@ def _from_storage_url(
8289 f"/v0/storage{ storage_url .path .removeprefix (prefix )} " , encoded = True
8390 ).with_scheme (request .headers .get (X_FORWARDED_PROTO , request .url .scheme ))
8491 )
85-
8692 if url_encode :
87- converted_url = converted_url .replace (url_encode , quote (url_encode , safe = "" ))
93+ converted_url = converted_url .replace (
94+ url_encode , quote (unquote (url_encode ), safe = "" )
95+ )
8896
8997 webserver_url : AnyUrl = TypeAdapter (AnyUrl ).validate_python (f"{ converted_url } " )
9098 return webserver_url
@@ -104,6 +112,8 @@ async def _forward_request_to_storage(
104112 async with session .request (
105113 method .upper (), url , ssl = False , json = body , ** kwargs
106114 ) as resp :
115+ if resp .status >= status .HTTP_400_BAD_REQUEST :
116+ raise web .HTTPException (reason = await resp .text ())
107117 payload = await resp .json ()
108118 return _ResponseTuple (payload = payload , status_code = resp .status )
109119
0 commit comments