1- use std::collections::BTreeMap;
1+ use std::collections::{ BTreeMap, HashMap} ;
22use std::sync::Arc;
33
44use async_trait::async_trait;
@@ -163,16 +163,13 @@ impl IntegrationRegistrationBuilder {
163163 }
164164}
165165
166- struct RegisteredRoute {
167- method: Method,
168- path: &'static str,
169- proxy: Arc<dyn IntegrationProxy>,
170- integration_id: &'static str,
171- }
166+ type RouteKey = (Method, String);
167+ type RouteValue = (Arc<dyn IntegrationProxy>, &'static str);
172168
173169#[derive(Default)]
174170struct IntegrationRegistryInner {
175- routes: Vec<RegisteredRoute>,
171+ route_map: HashMap<RouteKey, RouteValue>,
172+ routes: Vec<(IntegrationEndpoint, &'static str)>,
176173 html_rewriters: Vec<Arc<dyn IntegrationAttributeRewriter>>,
177174 script_rewriters: Vec<Arc<dyn IntegrationScriptRewriter>>,
178175 assets: Vec<(&'static str, String)>,
@@ -215,12 +212,20 @@ impl IntegrationRegistry {
215212 if let Some(registration) = builder(settings) {
216213 for proxy in registration.proxies {
217214 for route in proxy.routes() {
218- inner.routes.push(RegisteredRoute {
219- method: route.method.clone(),
220- path: route.path,
221- proxy: proxy.clone(),
222- integration_id: registration.integration_id,
223- });
215+ if inner
216+ .route_map
217+ .insert(
218+ (route.method.clone(), route.path.to_string()),
219+ (proxy.clone(), registration.integration_id),
220+ )
221+ .is_some()
222+ {
223+ panic!(
224+ "Integration route collision detected for {} {}",
225+ route.method, route.path
226+ );
227+ }
228+ inner.routes.push((route, registration.integration_id));
224229 }
225230 }
226231 inner
@@ -246,9 +251,8 @@ impl IntegrationRegistry {
246251 /// Return true when any proxy is registered for the provided route.
247252 pub fn has_route(&self, method: &Method, path: &str) -> bool {
248253 self.inner
249- .routes
250- .iter()
251- .any(|r| r.method == method && r.path == path)
254+ .route_map
255+ .contains_key(&(method.clone(), path.to_string()))
252256 }
253257
254258 /// Dispatch a proxy request when an integration handles the path.
@@ -259,12 +263,15 @@ impl IntegrationRegistry {
259263 settings: &Settings,
260264 req: Request,
261265 ) -> Option<Result<Response, Report<TrustedServerError>>> {
262- for route in &self.inner.routes {
263- if route.method == method && route.path == path {
264- return Some(route.proxy.handle(settings, req).await);
265- }
266+ if let Some((proxy, _)) = self
267+ .inner
268+ .route_map
269+ .get(&(method.clone(), path.to_string()))
270+ {
271+ Some(proxy.handle(settings, req).await)
272+ } else {
273+ None
266274 }
267- None
268275 }
269276
270277 /// Give integrations a chance to rewrite HTML attributes.
@@ -302,10 +309,10 @@ impl IntegrationRegistry {
302309 pub fn registered_integrations(&self) -> Vec<IntegrationMetadata> {
303310 let mut map: BTreeMap<&'static str, IntegrationMetadata> = BTreeMap::new();
304311
305- for route in &self.inner.routes {
312+ for ( route, integration_id) in &self.inner.routes {
306313 let entry = map
307- .entry(route. integration_id)
308- .or_insert_with(|| IntegrationMetadata::new(route. integration_id));
314+ .entry(* integration_id)
315+ .or_insert_with(|| IntegrationMetadata::new(integration_id));
309316 entry
310317 .routes
311318 .push(IntegrationEndpoint::new(route.method.clone(), route.path));
0 commit comments