Skip to content

Commit

Permalink
fix: writing byte one at a time
Browse files Browse the repository at this point in the history
  • Loading branch information
panuhorsmalahti committed Jan 16, 2025
1 parent 53164e7 commit f6eae85
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 9 deletions.
11 changes: 11 additions & 0 deletions src/__tests__/__snapshots__/stream-impersonator.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ impersonate-user: johndoe
BarÄö"
`;

exports[`StreamImpersonator handles binary data in body one byte at a time 1`] = `
"POST / HTTP/1.1
accept: application/json
content-type: application/octet-stream
content-length: 4
authorization: Bearer service-account-token
impersonate-user: johndoe
Äö"
`;

exports[`StreamImpersonator handles body separator splitted to separate chunks 1`] = `
"GET / HTTP/1.1
accept: application/json
Expand Down
37 changes: 37 additions & 0 deletions src/__tests__/stream-impersonator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,43 @@ MwIDAQAB
expect(destination.buffer.includes(Buffer.from([0x01, 0x02, 0x03, 0x42, 0x61, 0x72, 0xC3, 0x84, 0xC3, 0xB6]))).toBe(true);
});

it("handles binary data in body one byte at a time", async () => {
parser.boredServer = boredServer;
parser.publicKey = jwtPublicKey;

const token = jwt.sign(
{
exp: Math.floor(Date.now() / 1000) + 60 * 60,
sub: "johndoe",
aud: [boredServer],
},
jwtPrivateKey,
{ algorithm: "RS256" }
);

stream.pipe(parser).pipe(destination);

// Writing the headers as text
stream.write("POST / HTTP/1.1\r\n");
stream.write("Accept: application/json\r\n");
stream.write(`Authorization: Bearer ${token}\r\n`);
stream.write("Content-Type: application/octet-stream\r\nContent-Length: 4\r\n\r\n");

// Writing binary data as four Buffer chunks
// UTF8: Äö
stream.write(Buffer.from([0xC3]));
await delay(1);
stream.write(Buffer.from([0x84]));
await delay(1);
stream.write(Buffer.from([0xC3]));
await delay(1);
stream.write(Buffer.from([0xB6]));

expect(destination.buffer.toString()).toMatchSnapshot();
expect(destination.buffer.toString()).toContain("Äö");
expect(destination.buffer.includes(Buffer.from([0xC3, 0x84, 0xC3, 0xB6]))).toBe(true);
});

it ("handles headers in one chunk, body in another", () => {
parser.boredServer = boredServer;
parser.publicKey = jwtPublicKey;
Expand Down
20 changes: 11 additions & 9 deletions src/stream-impersonator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class StreamImpersonator extends Transform {
private httpParser: HTTPParserJS;
private upgrade = false;
private getSaToken: GetSaToken;
private partialMessage: string = "";
private partialMessage: Buffer[] = [];

constructor(getSaToken: GetSaToken) {
super();
Expand Down Expand Up @@ -103,32 +103,34 @@ export class StreamImpersonator extends Transform {
encoding: BufferEncoding,
callback: TransformCallback,
): void {
const chunkStr = chunk.toString();

if (this.upgrade) {
this.push(chunk);

return callback();
}

this.partialMessage += chunkStr;
this.partialMessage.push(chunk);

const handleError = (err: Error) => {
this.partialMessage = "";
this.partialMessage = [];
logger.error("[IMPERSONATOR] Error parsing HTTP data: %s", String(err));
throw err;
};

try {
const bytesParsed = this.httpParser.execute(
Buffer.from(this.partialMessage),
);
const bufferToParse = Buffer.concat(this.partialMessage);
const bytesParsed = this.httpParser.execute(bufferToParse);

if (bytesParsed !== bufferToParse.length) {
logger.warn("Only " + bytesParsed + "/" + bufferToParse.length + " bytes parsed of the chunk");

Check failure on line 125 in src/stream-impersonator.ts

View workflow job for this annotation

GitHub Actions / Test (20.x)

Unexpected string concatenation
}

if (bytesParsed instanceof Error) {
return handleError(bytesParsed);
}

this.partialMessage = this.partialMessage.slice(bytesParsed);
// TODO: We assume the whole chunk is always parsed
this.partialMessage.pop();
} catch (err) {
return handleError(err as Error);
}
Expand Down

0 comments on commit f6eae85

Please sign in to comment.