@@ -33,6 +33,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
3333 private readonly peerRouting : PeerRouting
3434 private readonly filterAddrs ?: string [ ]
3535 private readonly filterProtocols ?: string [ ]
36+ private readonly inFlightRequests : Map < string , Promise < Response > >
3637
3738 /**
3839 * Create a new DelegatedContentRouting instance
@@ -44,6 +45,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
4445 this . httpQueue = new PQueue ( {
4546 concurrency : init . concurrentRequests ?? defaultValues . concurrentRequests
4647 } )
48+ this . inFlightRequests = new Map ( ) // Tracks in-flight requests to avoid duplicate requests
4749 this . clientUrl = url instanceof URL ? url : new URL ( url )
4850 this . timeout = init . timeout ?? defaultValues . timeout
4951 this . filterAddrs = init . filterAddrs
@@ -95,7 +97,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
9597 const url = new URL ( `${ this . clientUrl } routing/v1/providers/${ cid . toString ( ) } ` )
9698 this . #addFilterParams( url , options . filterAddrs , options . filterProtocols )
9799 const getOptions = { headers : { Accept : 'application/x-ndjson' } , signal }
98- const res = await fetch ( url , getOptions )
100+ const res = await this . #makeRequest ( url . toString ( ) , getOptions )
99101
100102 if ( res . status === 404 ) {
101103 // https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
@@ -162,7 +164,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
162164 this . #addFilterParams( url , options . filterAddrs , options . filterProtocols )
163165
164166 const getOptions = { headers : { Accept : 'application/x-ndjson' } , signal }
165- const res = await fetch ( url , getOptions )
167+ const res = await this . #makeRequest ( url . toString ( ) , getOptions )
166168
167169 if ( res . status === 404 ) {
168170 // https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
@@ -228,7 +230,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
228230 await onStart . promise
229231
230232 const getOptions = { headers : { Accept : 'application/vnd.ipfs.ipns-record' } , signal }
231- const res = await fetch ( resource , getOptions )
233+ const res = await this . #makeRequest ( resource , getOptions )
232234
233235 log ( 'getIPNS GET %s %d' , resource , res . status )
234236
@@ -290,7 +292,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
290292 const body = marshalIPNSRecord ( record )
291293
292294 const getOptions = { method : 'PUT' , headers : { 'Content-Type' : 'application/vnd.ipfs.ipns-record' } , body, signal }
293- const res = await fetch ( resource , getOptions )
295+ const res = await this . #makeRequest ( resource , getOptions )
294296
295297 log ( 'putIPNS PUT %s %d' , resource , res . status )
296298
@@ -349,4 +351,24 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
349351 }
350352 }
351353 }
354+
355+ // Ensures that only one concurrent request is made for the same URL with the same method
356+ async #makeRequest ( url : string , options : RequestInit ) : Promise < Response > {
357+ const key = `${ options . method ?? 'GET' } -${ url } `
358+
359+ // Check if there's already an in-flight request for this URL
360+ const existingRequest = this . inFlightRequests . get ( key )
361+ if ( existingRequest != null ) {
362+ return existingRequest
363+ }
364+
365+ // Create new request and track it
366+ const requestPromise = fetch ( url , options ) . finally ( ( ) => {
367+ // Clean up the tracked request when it completes
368+ this . inFlightRequests . delete ( key )
369+ } )
370+
371+ this . inFlightRequests . set ( key , requestPromise )
372+ return requestPromise
373+ }
352374}
0 commit comments