Skip to content
Merged
10 changes: 10 additions & 0 deletions apps/sim/blocks/blocks/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ Example:
generationType: 'json-object',
},
},
{
id: 'timeout',
title: 'Timeout (ms)',
type: 'short-input',
placeholder: '300000',
description:
'Request timeout in milliseconds (default: 300000 = 5 minutes, max: 600000 = 10 minutes)',
mode: 'advanced',
},
],
tools: {
access: ['http_request'],
Expand All @@ -90,6 +99,7 @@ Example:
headers: { type: 'json', description: 'Request headers' },
body: { type: 'json', description: 'Request body data' },
params: { type: 'json', description: 'URL query parameters' },
timeout: { type: 'number', description: 'Request timeout in milliseconds' },
},
outputs: {
data: { type: 'json', description: 'API response data (JSON, text, or other formats)' },
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/lib/core/security/input-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ export async function secureFetchWithPinnedIP(
method: options.method || 'GET',
headers: sanitizedHeaders,
agent,
timeout: options.timeout || 30000,
timeout: options.timeout || 300000, // Default 5 minutes
Comment thread
waleedlatif1 marked this conversation as resolved.
}

const protocol = isHttps ? https : http
Expand Down
5 changes: 5 additions & 0 deletions apps/sim/tools/http/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export const requestTool: ToolConfig<RequestParams, RequestResponse> = {
visibility: 'user-or-llm',
description: 'Form data to send (will set appropriate Content-Type)',
},
timeout: {
type: 'number',
visibility: 'user-only',
description: 'Request timeout in milliseconds (default: 300000 = 5 minutes)',
},
},

request: {
Expand Down
1 change: 1 addition & 0 deletions apps/sim/tools/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface RequestParams {
params?: TableRow[]
pathParams?: Record<string, string>
formData?: Record<string, string | Blob>
timeout?: number
}

export interface RequestResponse extends ToolResponse {
Expand Down
29 changes: 24 additions & 5 deletions apps/sim/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,11 +625,29 @@ async function executeToolRequest(
let response: Response

if (isInternalRoute) {
response = await fetch(fullUrl, {
method: requestParams.method,
headers: headers,
body: requestParams.body,
})
// Set up AbortController for timeout support on internal routes
const controller = new AbortController()
const timeoutId = requestParams.timeout
? setTimeout(() => controller.abort(), requestParams.timeout)
: undefined
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated

try {
response = await fetch(fullUrl, {
method: requestParams.method,
headers: headers,
body: requestParams.body,
signal: controller.signal,
})
} catch (error) {
if (timeoutId) clearTimeout(timeoutId)
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
// Convert AbortError to a timeout error message
if (error instanceof Error && error.name === 'AbortError') {
throw new Error(`Request timed out after ${requestParams.timeout}ms`)
}
throw error
} finally {
if (timeoutId) clearTimeout(timeoutId)
}
} else {
const urlValidation = await validateUrlWithDNS(fullUrl, 'toolUrl')
if (!urlValidation.isValid) {
Expand All @@ -640,6 +658,7 @@ async function executeToolRequest(
method: requestParams.method,
headers: headersRecord,
body: requestParams.body ?? undefined,
timeout: requestParams.timeout,
})

const responseHeaders = new Headers(secureResponse.headers.toRecord())
Expand Down
8 changes: 7 additions & 1 deletion apps/sim/tools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ interface RequestParams {
method: string
headers: Record<string, string>
body?: string
timeout?: number
}

/**
Expand Down Expand Up @@ -122,7 +123,12 @@ export function formatRequestParams(tool: ToolConfig, params: Record<string, any
}
}

return { url, method, headers, body }
// Get timeout from params (if specified) and ensure it's a number
// The short-input subBlock returns a string, so we need to convert it
const rawTimeout = params.timeout
const timeout = rawTimeout != null ? Number(rawTimeout) : undefined

return { url, method, headers, body, timeout: Number.isNaN(timeout) ? undefined : timeout }
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
}

/**
Expand Down