2025-07-27 21:23:20 +00:00
import * as plugins from './plugins.js' ;
import * as types from './types.js' ;
2025-07-28 14:45:47 +00:00
import { CoreResponse } from './response.js' ;
2025-07-28 15:12:11 +00:00
import { CoreRequest as AbstractCoreRequest } from '../core_base/request.js' ;
2025-07-27 21:23:20 +00:00
// Keep-alive agents for connection pooling
2025-07-28 14:30:27 +00:00
const httpAgent = new plugins . agentkeepalive . HttpAgent ( {
2025-07-27 21:23:20 +00:00
keepAlive : true ,
maxFreeSockets : 10 ,
maxSockets : 100 ,
maxTotalSockets : 1000 ,
} ) ;
2025-07-28 14:30:27 +00:00
const httpAgentKeepAliveFalse = new plugins . agentkeepalive . HttpAgent ( {
2025-07-27 21:23:20 +00:00
keepAlive : false ,
} ) ;
const httpsAgent = new plugins . agentkeepalive . HttpsAgent ( {
keepAlive : true ,
maxFreeSockets : 10 ,
maxSockets : 100 ,
maxTotalSockets : 1000 ,
} ) ;
const httpsAgentKeepAliveFalse = new plugins . agentkeepalive . HttpsAgent ( {
keepAlive : false ,
} ) ;
/ * *
2025-07-28 15:12:11 +00:00
* Node . js implementation of Core Request class that handles all HTTP / HTTPS requests
2025-07-27 21:23:20 +00:00
* /
2025-07-28 15:12:11 +00:00
export class CoreRequest extends AbstractCoreRequest < types.ICoreRequestOptions , CoreResponse > {
2025-07-28 14:30:27 +00:00
private requestDataFunc : ( ( req : plugins.http.ClientRequest ) = > void ) | null ;
constructor (
url : string ,
options : types.ICoreRequestOptions = { } ,
requestDataFunc : ( ( req : plugins.http.ClientRequest ) = > void ) | null = null
) {
2025-07-28 15:12:11 +00:00
super ( url , options ) ;
2025-07-28 14:30:27 +00:00
this . requestDataFunc = requestDataFunc ;
2025-07-28 22:37:36 +00:00
// Check for unsupported fetch-specific options
if ( options . credentials || options . mode || options . cache || options . redirect ||
options . referrer || options . referrerPolicy || options . integrity ) {
throw new Error ( 'Fetch API specific options (credentials, mode, cache, redirect, referrer, referrerPolicy, integrity) are not supported in Node.js implementation' ) ;
}
2025-07-27 21:23:20 +00:00
}
2025-07-28 14:30:27 +00:00
/ * *
2025-07-28 14:45:47 +00:00
* Fire the request and return a CoreResponse
2025-07-28 14:30:27 +00:00
* /
2025-07-28 14:45:47 +00:00
async fire ( ) : Promise < CoreResponse > {
const incomingMessage = await this . fireCore ( ) ;
return new CoreResponse ( incomingMessage , this . url ) ;
2025-07-27 21:23:20 +00:00
}
2025-07-28 14:30:27 +00:00
/ * *
2025-07-28 14:45:47 +00:00
* Fire the request and return the raw IncomingMessage
2025-07-28 14:30:27 +00:00
* /
2025-07-28 14:45:47 +00:00
async fireCore ( ) : Promise < plugins.http.IncomingMessage > {
2025-07-28 14:30:27 +00:00
const done = plugins . smartpromise . defer < plugins.http.IncomingMessage > ( ) ;
// Parse URL
const parsedUrl = plugins . smarturl . Smarturl . createFromUrl ( this . url , {
searchParams : this.options.queryParams || { } ,
} ) ;
this . options . hostname = parsedUrl . hostname ;
if ( parsedUrl . port ) {
this . options . port = parseInt ( parsedUrl . port , 10 ) ;
}
this . options . path = parsedUrl . path ;
2025-07-27 21:23:20 +00:00
2025-07-28 14:30:27 +00:00
// Handle unix socket URLs
2025-07-28 14:45:47 +00:00
if ( CoreRequest . isUnixSocket ( this . url ) ) {
const { socketPath , path } = CoreRequest . parseUnixSocketUrl ( this . options . path ) ;
2025-07-28 14:30:27 +00:00
this . options . socketPath = socketPath ;
this . options . path = path ;
}
2025-07-27 21:23:20 +00:00
2025-07-28 14:30:27 +00:00
// Determine agent based on protocol and keep-alive setting
if ( ! this . options . agent ) {
// Only use keep-alive agents if explicitly requested
if ( this . options . keepAlive === true ) {
this . options . agent = parsedUrl . protocol === 'https:' ? httpsAgent : httpAgent ;
} else if ( this . options . keepAlive === false ) {
this . options . agent = parsedUrl . protocol === 'https:' ? httpsAgentKeepAliveFalse : httpAgentKeepAliveFalse ;
}
// If keepAlive is undefined, don't set any agent (more fetch-like behavior)
2025-07-27 21:23:20 +00:00
}
2025-07-28 14:30:27 +00:00
// Determine request module
const requestModule = parsedUrl . protocol === 'https:' ? plugins.https : plugins.http ;
2025-07-27 21:23:20 +00:00
2025-07-28 14:30:27 +00:00
if ( ! requestModule ) {
throw new Error ( ` The request to ${ this . url } is missing a viable protocol. Must be http or https ` ) ;
}
// Perform the request
const request = requestModule . request ( this . options , async ( response ) = > {
// Handle hard timeout
if ( this . options . hardDataCuttingTimeout ) {
setTimeout ( ( ) = > {
response . destroy ( ) ;
done . reject ( new Error ( 'Request timed out' ) ) ;
} , this . options . hardDataCuttingTimeout ) ;
}
// Always return the raw stream
done . resolve ( response ) ;
} ) ;
// Write request body
if ( this . options . requestBody ) {
if ( this . options . requestBody instanceof plugins . formData ) {
this . options . requestBody . pipe ( request ) . on ( 'finish' , ( ) = > {
request . end ( ) ;
} ) ;
} else {
// Write body as-is - caller is responsible for serialization
const bodyData = typeof this . options . requestBody === 'string'
? this . options . requestBody
: this . options . requestBody instanceof Buffer
? this . options . requestBody
: JSON . stringify ( this . options . requestBody ) ; // Still stringify for backward compatibility
request . write ( bodyData ) ;
2025-07-27 21:23:20 +00:00
request . end ( ) ;
2025-07-28 14:30:27 +00:00
}
} else if ( this . requestDataFunc ) {
this . requestDataFunc ( request ) ;
2025-07-27 21:23:20 +00:00
} else {
request . end ( ) ;
}
2025-07-28 14:30:27 +00:00
// Handle request errors
request . on ( 'error' , ( e ) = > {
console . error ( e ) ;
request . destroy ( ) ;
done . reject ( e ) ;
} ) ;
// Get response and handle response errors
const response = await done . promise ;
response . on ( 'error' , ( err ) = > {
console . error ( err ) ;
response . destroy ( ) ;
} ) ;
return response ;
2025-07-27 21:23:20 +00:00
}
2025-07-28 14:30:27 +00:00
/ * *
2025-07-28 14:45:47 +00:00
* Static factory method to create and fire a request
2025-07-28 14:30:27 +00:00
* /
static async create (
url : string ,
options : types.ICoreRequestOptions = { }
2025-07-28 14:45:47 +00:00
) : Promise < CoreResponse > {
const request = new CoreRequest ( url , options ) ;
return request . fire ( ) ;
2025-07-28 14:30:27 +00:00
}
}