11"""Tooling to manage the reverse proxying of requests to an upstream STAC API."""
22
3- import json
43import logging
54import time
6- from dataclasses import dataclass
7- from typing import Annotated , Callable , Optional
5+ from dataclasses import dataclass , field
86
97import httpx
10- from cql2 import Expr
11- from fastapi import Depends , Request
8+ from fastapi import Request
129from starlette .background import BackgroundTask
1310from starlette .datastructures import MutableHeaders
1411from starlette .responses import StreamingResponse
1512
16- from ..utils import di , filters
1713
1814logger = logging .getLogger (__name__ )
1915
@@ -24,79 +20,35 @@ class ReverseProxyHandler:
2420
2521 upstream : str
2622 client : httpx .AsyncClient = None
27-
28- # Filters
29- collections_filter : Optional [Callable ] = None
30- items_filter : Optional [Callable ] = None
23+ timeout : httpx .Timeout = field (default_factory = lambda : httpx .Timeout (timeout = 15.0 ))
3124
3225 def __post_init__ (self ):
3326 """Initialize the HTTP client."""
3427 self .client = self .client or httpx .AsyncClient (
3528 base_url = self .upstream ,
36- timeout = httpx .Timeout (timeout = 15.0 ),
37- )
38- self .collections_filter = (
39- self .collections_filter () if self .collections_filter else None
29+ timeout = self .timeout ,
4030 )
41- self .items_filter = self .items_filter () if self .items_filter else None
4231
43- def _get_filter (self , path : str ) -> Optional [Callable [..., Expr ]]:
44- """Get the CQL2 filter builder for the given path."""
45- endpoint_filters = [
46- # TODO: Use collections_filter_endpoints & items_filter_endpoints
47- (filters .is_collection_endpoint , self .collections_filter ),
48- (filters .is_item_endpoint , self .items_filter ),
49- (filters .is_search_endpoint , self .items_filter ),
50- ]
51- for check , builder in endpoint_filters :
52- if check (path ):
53- return builder
54- return None
55-
56- async def proxy_request (self , request : Request , * , stream = False ) -> httpx .Response :
32+ async def proxy_request (self , request : Request ) -> httpx .Response :
5733 """Proxy a request to the upstream STAC API."""
5834 headers = MutableHeaders (request .headers )
5935 headers .setdefault ("X-Forwarded-For" , request .client .host )
6036 headers .setdefault ("X-Forwarded-Host" , request .url .hostname )
6137
62- path = request .url .path
63- query = request .url .query
64- # TODO: Should we only do this conditionally based on stream?
65- body = (await request .body ()).decode ()
66-
67- # Apply filter if applicable
68- filter_builder = self ._get_filter (path )
69- if filter_builder :
70- cql_filter = await di .call_with_injected_dependencies (
71- func = filter_builder ,
72- request = request ,
73- )
74- cql_filter .validate ()
75-
76- if request .method == "GET" :
77- query = filters .insert_filter (qs = query , filter = cql_filter )
78- elif request .method in ["POST" , "PUT" ]:
79- body_dict = json .loads (body )
80- body_filter = body_dict .get ("filter" )
81- if body_filter :
82- cql_filter = cql_filter + Expr (body_filter )
83- body_dict ["filter" ] = cql_filter .to_json ()
84- body = json .dumps (body_dict )
85-
8638 # https://github.com/fastapi/fastapi/discussions/7382#discussioncomment-5136466
8739 rp_req = self .client .build_request (
8840 request .method ,
8941 url = httpx .URL (
90- path = path ,
91- query = query .encode ("utf-8" ),
42+ path = request . url . path ,
43+ query = request . url . query .encode ("utf-8" ),
9244 ),
9345 headers = headers ,
94- content = body ,
46+ content = request . stream () ,
9547 )
9648 logger .debug (f"Proxying request to { rp_req .url } " )
9749
9850 start_time = time .perf_counter ()
99- rp_resp = await self .client .send (rp_req , stream = stream )
51+ rp_resp = await self .client .send (rp_req , stream = True )
10052 proxy_time = time .perf_counter () - start_time
10153
10254 logger .debug (
@@ -107,11 +59,7 @@ async def proxy_request(self, request: Request, *, stream=False) -> httpx.Respon
10759
10860 async def stream (self , request : Request ) -> StreamingResponse :
10961 """Transparently proxy a request to the upstream STAC API."""
110- rp_resp = await self .proxy_request (
111- request ,
112- # collections_filter=collections_filter,
113- stream = True ,
114- )
62+ rp_resp = await self .proxy_request (request )
11563 return StreamingResponse (
11664 rp_resp .aiter_raw (),
11765 status_code = rp_resp .status_code ,
0 commit comments