11use std:: sync:: Arc ;
2+ use std:: time:: Duration ;
23
34use crate :: executors:: common:: HttpExecutionResponse ;
45use crate :: executors:: dedupe:: { request_fingerprint, ABuildHasher , SharedResponse } ;
56use dashmap:: DashMap ;
7+ use futures:: TryFutureExt ;
68use tokio:: sync:: OnceCell ;
79
810use async_trait:: async_trait;
@@ -18,7 +20,7 @@ use hyper_util::client::legacy::{connect::HttpConnector, Client};
1820use tokio:: sync:: Semaphore ;
1921use tracing:: debug;
2022
21- use crate :: executors:: common:: HttpExecutionRequest ;
23+ use crate :: executors:: common:: SubgraphExecutionRequest ;
2224use crate :: executors:: error:: SubgraphExecutorError ;
2325use crate :: response:: graphql_error:: GraphQLError ;
2426use crate :: utils:: consts:: CLOSE_BRACE ;
@@ -75,7 +77,7 @@ impl HTTPSubgraphExecutor {
7577
7678 fn build_request_body (
7779 & self ,
78- execution_request : & HttpExecutionRequest < ' _ > ,
80+ execution_request : & SubgraphExecutionRequest < ' _ > ,
7981 ) -> Result < Vec < u8 > , SubgraphExecutorError > {
8082 let mut body = Vec :: with_capacity ( 4096 ) ;
8183 body. put ( FIRST_QUOTE_STR ) ;
@@ -136,6 +138,7 @@ impl HTTPSubgraphExecutor {
136138 & self ,
137139 body : Vec < u8 > ,
138140 headers : HeaderMap ,
141+ timeout : Option < Duration > ,
139142 ) -> Result < SharedResponse , SubgraphExecutorError > {
140143 let mut req = hyper:: Request :: builder ( )
141144 . method ( http:: Method :: POST )
@@ -150,9 +153,22 @@ impl HTTPSubgraphExecutor {
150153
151154 debug ! ( "making http request to {}" , self . endpoint. to_string( ) ) ;
152155
153- let res = self . http_client . request ( req) . await . map_err ( |e| {
156+ let res_fut = self . http_client . request ( req) . map_err ( |e| {
154157 SubgraphExecutorError :: RequestFailure ( self . endpoint . to_string ( ) , e. to_string ( ) )
155- } ) ?;
158+ } ) ;
159+
160+ let res = if let Some ( timeout_duration) = timeout {
161+ tokio:: time:: timeout ( timeout_duration, res_fut)
162+ . await
163+ . map_err ( |_| {
164+ SubgraphExecutorError :: RequestTimeout (
165+ self . endpoint . to_string ( ) ,
166+ timeout_duration. as_secs ( ) ,
167+ )
168+ } ) ?
169+ } else {
170+ res_fut. await
171+ } ?;
156172
157173 debug ! (
158174 "http request to {} completed, status: {}" ,
@@ -211,7 +227,8 @@ impl SubgraphExecutor for HTTPSubgraphExecutor {
211227 #[ tracing:: instrument( skip_all, fields( subgraph_name = %self . subgraph_name) ) ]
212228 async fn execute < ' a > (
213229 & self ,
214- execution_request : HttpExecutionRequest < ' a > ,
230+ execution_request : SubgraphExecutionRequest < ' a > ,
231+ timeout : Option < Duration > ,
215232 ) -> HttpExecutionResponse {
216233 let body = match self . build_request_body ( & execution_request) {
217234 Ok ( body) => body,
@@ -233,7 +250,7 @@ impl SubgraphExecutor for HTTPSubgraphExecutor {
233250 // This unwrap is safe because the semaphore is never closed during the application's lifecycle.
234251 // `acquire()` only fails if the semaphore is closed, so this will always return `Ok`.
235252 let _permit = self . semaphore . acquire ( ) . await . unwrap ( ) ;
236- return match self . _send_request ( body, headers) . await {
253+ return match self . _send_request ( body, headers, timeout ) . await {
237254 Ok ( shared_response) => HttpExecutionResponse {
238255 body : shared_response. body ,
239256 headers : shared_response. headers ,
@@ -265,7 +282,7 @@ impl SubgraphExecutor for HTTPSubgraphExecutor {
265282 // This unwrap is safe because the semaphore is never closed during the application's lifecycle.
266283 // `acquire()` only fails if the semaphore is closed, so this will always return `Ok`.
267284 let _permit = self . semaphore . acquire ( ) . await . unwrap ( ) ;
268- self . _send_request ( body, headers) . await
285+ self . _send_request ( body, headers, timeout ) . await
269286 } ;
270287 // It's important to remove the entry from the map before returning the result.
271288 // This ensures that once the OnceCell is set, no future requests can join it.
0 commit comments