Skip to content

Commit 109caf1

Browse files
netroyriascho
authored andcommitted
fix(HTTP Request Node): Use iconv-lite to decode http responses, to support more encoding types (#11930)
1 parent 69cbf9b commit 109caf1

File tree

7 files changed

+132
-34
lines changed

7 files changed

+132
-34
lines changed

Diff for: packages/@n8n/imap/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"dist/**/*"
2121
],
2222
"dependencies": {
23-
"iconv-lite": "0.6.3",
23+
"iconv-lite": "catalog:",
2424
"imap": "0.8.19",
2525
"quoted-printable": "1.0.1",
2626
"utf8": "3.0.0",

Diff for: packages/core/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"fast-glob": "catalog:",
4848
"file-type": "16.5.4",
4949
"form-data": "catalog:",
50+
"iconv-lite": "catalog:",
5051
"lodash": "catalog:",
5152
"luxon": "catalog:",
5253
"mime-types": "2.1.35",

Diff for: packages/core/src/NodeExecuteFunctions.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { createReadStream } from 'fs';
2828
import { access as fsAccess, writeFile as fsWriteFile } from 'fs/promises';
2929
import { IncomingMessage } from 'http';
3030
import { Agent, type AgentOptions } from 'https';
31+
import iconv from 'iconv-lite';
3132
import get from 'lodash/get';
3233
import isEmpty from 'lodash/isEmpty';
3334
import merge from 'lodash/merge';
@@ -745,13 +746,13 @@ export function parseIncomingMessage(message: IncomingMessage) {
745746
}
746747
}
747748

748-
export async function binaryToString(body: Buffer | Readable, encoding?: BufferEncoding) {
749-
const buffer = await binaryToBuffer(body);
749+
export async function binaryToString(body: Buffer | Readable, encoding?: string) {
750750
if (!encoding && body instanceof IncomingMessage) {
751751
parseIncomingMessage(body);
752752
encoding = body.encoding;
753753
}
754-
return buffer.toString(encoding);
754+
const buffer = await binaryToBuffer(body);
755+
return iconv.decode(buffer, encoding ?? 'utf-8');
755756
}
756757

757758
export async function proxyRequestToAxios(

Diff for: packages/core/test/NodeExecuteFunctions.test.ts

+98-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mkdtempSync, readFileSync } from 'fs';
2-
import type { IncomingMessage } from 'http';
2+
import { IncomingMessage } from 'http';
33
import type { Agent } from 'https';
44
import { mock } from 'jest-mock-extended';
55
import type {
@@ -16,12 +16,14 @@ import type {
1616
import nock from 'nock';
1717
import { tmpdir } from 'os';
1818
import { join } from 'path';
19+
import { Readable } from 'stream';
1920
import type { SecureContextOptions } from 'tls';
2021
import Container from 'typedi';
2122

2223
import { BinaryDataService } from '@/BinaryData/BinaryData.service';
2324
import { InstanceSettings } from '@/InstanceSettings';
2425
import {
26+
binaryToString,
2527
copyInputItems,
2628
getBinaryDataBuffer,
2729
isFilePathBlocked,
@@ -549,6 +551,101 @@ describe('NodeExecuteFunctions', () => {
549551
},
550552
);
551553
});
554+
555+
describe('binaryToString', () => {
556+
const ENCODING_SAMPLES = {
557+
utf8: {
558+
text: 'Hello, 世界! τεστ мир ⚡️ é à ü ñ',
559+
buffer: Buffer.from([
560+
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x21, 0x20,
561+
0xcf, 0x84, 0xce, 0xb5, 0xcf, 0x83, 0xcf, 0x84, 0x20, 0xd0, 0xbc, 0xd0, 0xb8, 0xd1, 0x80,
562+
0x20, 0xe2, 0x9a, 0xa1, 0xef, 0xb8, 0x8f, 0x20, 0xc3, 0xa9, 0x20, 0xc3, 0xa0, 0x20, 0xc3,
563+
0xbc, 0x20, 0xc3, 0xb1,
564+
]),
565+
},
566+
567+
'iso-8859-15': {
568+
text: 'Café € personnalité',
569+
buffer: Buffer.from([
570+
0x43, 0x61, 0x66, 0xe9, 0x20, 0xa4, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x6e, 0x61,
571+
0x6c, 0x69, 0x74, 0xe9,
572+
]),
573+
},
574+
575+
latin1: {
576+
text: 'señor année déjà',
577+
buffer: Buffer.from([
578+
0x73, 0x65, 0xf1, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x6e, 0xe9, 0x65, 0x20, 0x64, 0xe9, 0x6a,
579+
0xe0,
580+
]),
581+
},
582+
583+
ascii: {
584+
text: 'Hello, World! 123',
585+
buffer: Buffer.from([
586+
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x20, 0x31,
587+
0x32, 0x33,
588+
]),
589+
},
590+
591+
'windows-1252': {
592+
text: '€ Smart "quotes" • bullet',
593+
buffer: Buffer.from([
594+
0x80, 0x20, 0x53, 0x6d, 0x61, 0x72, 0x74, 0x20, 0x22, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73,
595+
0x22, 0x20, 0x95, 0x20, 0x62, 0x75, 0x6c, 0x6c, 0x65, 0x74,
596+
]),
597+
},
598+
599+
'shift-jis': {
600+
text: 'こんにちは世界',
601+
buffer: Buffer.from([
602+
0x82, 0xb1, 0x82, 0xf1, 0x82, 0xc9, 0x82, 0xbf, 0x82, 0xcd, 0x90, 0xa2, 0x8a, 0x45,
603+
]),
604+
},
605+
606+
big5: {
607+
text: '哈囉世界',
608+
buffer: Buffer.from([0xab, 0xa2, 0xc5, 0x6f, 0xa5, 0x40, 0xac, 0xc9]),
609+
},
610+
611+
'koi8-r': {
612+
text: 'Привет мир',
613+
buffer: Buffer.from([0xf0, 0xd2, 0xc9, 0xd7, 0xc5, 0xd4, 0x20, 0xcd, 0xc9, 0xd2]),
614+
},
615+
};
616+
617+
describe('should handle Buffer', () => {
618+
for (const [encoding, { text, buffer }] of Object.entries(ENCODING_SAMPLES)) {
619+
test(`with ${encoding}`, async () => {
620+
const data = await binaryToString(buffer, encoding);
621+
expect(data).toBe(text);
622+
});
623+
}
624+
});
625+
626+
describe('should handle streams', () => {
627+
for (const [encoding, { text, buffer }] of Object.entries(ENCODING_SAMPLES)) {
628+
test(`with ${encoding}`, async () => {
629+
const stream = Readable.from(buffer);
630+
const data = await binaryToString(stream, encoding);
631+
expect(data).toBe(text);
632+
});
633+
}
634+
});
635+
636+
describe('should handle IncomingMessage', () => {
637+
for (const [encoding, { text, buffer }] of Object.entries(ENCODING_SAMPLES)) {
638+
test(`with ${encoding}`, async () => {
639+
const response = Readable.from(buffer) as IncomingMessage;
640+
response.headers = { 'content-type': `application/json;charset=${encoding}` };
641+
// @ts-expect-error need this hack to fake `instanceof IncomingMessage` checks
642+
response.__proto__ = IncomingMessage.prototype;
643+
const data = await binaryToString(response);
644+
expect(data).toBe(text);
645+
});
646+
}
647+
});
648+
});
552649
});
553650

554651
describe('isFilePathBlocked', () => {

Diff for: packages/nodes-base/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@
868868
"get-system-fonts": "2.0.2",
869869
"gm": "1.25.0",
870870
"html-to-text": "9.0.5",
871-
"iconv-lite": "0.6.3",
871+
"iconv-lite": "catalog:",
872872
"ics": "2.40.0",
873873
"isbot": "3.6.13",
874874
"iso-639-1": "2.1.15",

Diff for: pnpm-lock.yaml

+26-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: pnpm-workspace.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ catalog:
1818
fast-glob: 3.2.12
1919
flatted: 3.2.7
2020
form-data: 4.0.0
21+
iconv-lite: 0.6.3
2122
lodash: 4.17.21
2223
luxon: 3.4.4
2324
nanoid: 3.3.6

0 commit comments

Comments
 (0)