diff --git a/src/jsonrpc.ts b/src/jsonrpc.ts index a07b942..255418d 100644 --- a/src/jsonrpc.ts +++ b/src/jsonrpc.ts @@ -241,6 +241,7 @@ interface TransportOptions { connectTimeout?: number | undefined; timeout?: number | undefined; useHttps?: boolean; + followRedirects?: boolean; } interface JsonRpcRequest { @@ -267,6 +268,8 @@ export class Transport { private connectTimeout: number; private timeout: number; private useHttps: boolean; + private followRedirects: boolean; + private redirectCount: number; constructor(options: TransportOptions = {}) { this.fingerprints = options.fingerprints || null; @@ -276,6 +279,8 @@ export class Transport { this.connectTimeout = options.connectTimeout || CONNECT_TIMEOUT; this.timeout = options.timeout || DEFAULT_TIMEOUT; this.useHttps = options.useHttps || false; + this.followRedirects = options.followRedirects ?? true; + this.redirectCount = 0; } /** @@ -291,6 +296,21 @@ export class Transport { handler: string, requestData: string, verbose: boolean = false + ): Promise { + // Reset redirect count for each new request + this.redirectCount = 0; + return this.doRequest(host, handler, requestData, verbose); + } + + /** + * Internal method to make HTTP request with redirect support + */ + private async doRequest( + host: string, + handler: string, + requestData: string, + verbose: boolean = false, + forceHttps?: boolean ): Promise { // Detect protocol based on port or explicit protocol const hostParts = host.split(":"); @@ -299,6 +319,7 @@ export class Transport { // Use HTTPS if explicitly configured, or for standard HTTPS ports const shouldUseHttps = + forceHttps === true || this.useHttps || port === 443 || port === 8443 || @@ -364,6 +385,49 @@ export class Transport { res.on("end", () => { try { + // Handle redirects BEFORE parsing response body + const statusCode = res.statusCode; + if ( + this.followRedirects && + statusCode && + this.redirectCount < 3 && + [301, 302, 307, 308].includes(statusCode) + ) { + const location = res.headers["location"]; + if (location) { + this.redirectCount++; + if (verbose) { + console.log( + `🔄 Redirect (${statusCode}): ${host}${handler} → ${location}` + ); + } + + try { + const redirectUrl = new URL( + location, + `http://${host}` + ); + const newHost = redirectUrl.host; + const newUseHttps = + redirectUrl.protocol === "https:"; + + this.doRequest( + newHost, + handler, + requestData, + verbose, + newUseHttps + ) + .then(resolve) + .catch(reject); + return; + } catch (redirectError) { + reject(redirectError); + return; + } + } + } + // Handle compression const encoding = res.headers["content-encoding"]; let responseText: string;