Skip to content

Commit

Permalink
Add tests for client error & httpPeekedData case
Browse files Browse the repository at this point in the history
  • Loading branch information
pimterry committed Dec 6, 2023
1 parent 65c09b4 commit 38dc555
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
36 changes: 35 additions & 1 deletion test/http.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as http from 'http';
import { expect } from 'chai';

import { Server } from '..';
import { Deferred, getDeferred, streamToBuffer } from './test-util';
import { Deferred, getDeferred, streamToBuffer, sendRawRequest } from './test-util';

describe("HTTP", () => {

Expand Down Expand Up @@ -68,4 +68,38 @@ describe("HTTP", () => {
socket.end();
});

it("should report client errors with full packet data", async () => {
serverReqRes.then(() => {
throw new Error("Request handler should not be called");
});

const serverErrorPromise = new Promise<any>((resolve) => {
// Multiple errors will be fired - we want to check the data from the final
// error (which will contain the whole packet)
let lastResult: any;

server.on('clientError', (err: any, socket: net.Socket) => {
socket.destroy();

lastResult = [err, socket];
setImmediate(() => resolve(lastResult));
});
});

sendRawRequest(server, 'QQQ http://example.com HTTP/1.1\r\n\r\n');

let [serverError, failedSocket] = await serverErrorPromise;

expect(serverError.message).to.include('Invalid method');

const combinedPacket = Buffer.concat([
failedSocket.__httpPeekedData,
serverError.rawPacket
].filter(Boolean));

expect(combinedPacket.toString('utf8')).to.equal(
'QQQ http://example.com HTTP/1.1\r\n\r\n'
);
});

});
36 changes: 35 additions & 1 deletion test/http2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as http2 from 'http2';
import { expect } from 'chai';

import { Server } from '..';
import { testKey, testCert, Deferred, getDeferred, streamToBuffer } from './test-util';
import { testKey, testCert, Deferred, getDeferred, streamToBuffer, sendRawRequest } from './test-util';


describe("HTTP/2", () => {
Expand Down Expand Up @@ -97,4 +97,38 @@ describe("HTTP/2", () => {
clientSession.close();
});

it("should report client errors with full packet data", async () => {
serverReqRes.then(() => {
throw new Error("Request handler should not be called");
});

const serverErrorPromise = new Promise<any>((resolve) => {
// Multiple errors will be fired - we want to check the data from the final
// error (which will contain the whole packet)
let lastResult: any;

server.on('clientError', (err: any, socket: net.Socket) => {
socket.destroy();

lastResult = [err, socket];
setImmediate(() => resolve(lastResult));
});
});

sendRawRequest(server, 'QQQ http://example.com HTTP/1.1\r\n\r\n');

let [serverError, failedSocket] = await serverErrorPromise;

expect(serverError.message).to.include('Invalid method');

const combinedPacket = Buffer.concat([
failedSocket.__httpPeekedData,
serverError.rawPacket
].filter(Boolean));

expect(combinedPacket.toString('utf8')).to.equal(
'QQQ http://example.com HTTP/1.1\r\n\r\n'
);
});

});
36 changes: 35 additions & 1 deletion test/https.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as https from 'https';
import { expect } from 'chai';

import { Server } from '..';
import { testKey, testCert, Deferred, getDeferred, streamToBuffer } from './test-util';
import { testKey, testCert, Deferred, getDeferred, streamToBuffer, sendRawRequest } from './test-util';

describe("HTTPS", () => {

Expand Down Expand Up @@ -113,4 +113,38 @@ describe("HTTPS", () => {
expect(serverTlsError.message).to.equal('socket hang up');
});

it("should report client errors with full packet data", async () => {
serverReqRes.then(() => {
throw new Error("Request handler should not be called");
});

const serverErrorPromise = new Promise<any>((resolve) => {
// Multiple errors will be fired - we want to check the data from the final
// error (which will contain the whole packet)
let lastResult: any;

server.on('clientError', (err: any, socket: net.Socket) => {
socket.destroy();

lastResult = [err, socket];
setImmediate(() => resolve(lastResult));
});
});

sendRawRequest(server, 'QQQ http://example.com HTTP/1.1\r\n\r\n');

let [serverError, failedSocket] = await serverErrorPromise;

expect(serverError.message).to.include('Invalid method');

const combinedPacket = Buffer.concat([
failedSocket.__httpPeekedData,
serverError.rawPacket
].filter(Boolean));

expect(combinedPacket.toString('utf8')).to.equal(
'QQQ http://example.com HTTP/1.1\r\n\r\n'
);
});

});
17 changes: 17 additions & 0 deletions test/test-util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as stream from 'stream';
import * as fs from 'fs';
import * as net from 'net';

export const testKey = fs.readFileSync(__dirname + '/fixtures/server.key');
export const testCert = fs.readFileSync(__dirname + '/fixtures/server.crt');
Expand Down Expand Up @@ -30,4 +31,20 @@ export async function streamToBuffer(stream: stream.Readable): Promise<Buffer> {
stream.on('end', () => resolve(Buffer.concat(data)));
stream.on('error', reject);
});
}

export async function sendRawRequest(server: net.Server, requestContent: string): Promise<string> {
const client = new net.Socket();
await new Promise<void>((resolve) => client.connect((server.address() as net.AddressInfo).port, '127.0.0.1', resolve));

const dataPromise = new Promise<string>((resolve) => {
client.on('data', function(data) {
resolve(data.toString());
client.destroy();
});
});

client.write(requestContent);
client.end();
return dataPromise;
}

0 comments on commit 38dc555

Please sign in to comment.