Skip to content

Commit

Permalink
test(node-http-handler): clean up ref()&unref() tests (#3554)
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP authored Apr 22, 2022
1 parent a5fa267 commit 3f1190c
Showing 1 changed file with 49 additions and 70 deletions.
119 changes: 49 additions & 70 deletions packages/node-http-handler/src/node-http2-handler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AbortController } from "@aws-sdk/abort-controller";
import { HttpRequest } from "@aws-sdk/protocol-http";
import { HttpRequest, HttpResponse } from "@aws-sdk/protocol-http";
import { rejects } from "assert";
import http2, { ClientHttp2Session, ClientHttp2Stream, constants, Http2Stream } from "http2";
import { Duplex } from "stream";
Expand Down Expand Up @@ -61,6 +61,25 @@ describe(NodeHttp2Handler.name, () => {
nodeH2Handler = new NodeHttp2Handler();
});

const closeConnection = async (response: HttpResponse) => {
const responseBody = response.body as ClientHttp2Stream;
const closePromise = new Promise((resolve) => responseBody.once("close", resolve));
responseBody.destroy();
await closePromise;
};

// Keeping node alive while request is open.
const expectSessionCreatedAndReferred = (session: ClientHttp2Session, requestCount = 1) => {
expect(session.ref).toHaveBeenCalledTimes(requestCount);
expect(session.unref).toHaveBeenCalledTimes(1);
};

// No longer keeping node alive
const expectSessionCreatedAndUnreffed = (session: ClientHttp2Session, requestCount = 1) => {
expect(session.ref).toHaveBeenCalledTimes(requestCount);
expect(session.unref).toHaveBeenCalledTimes(requestCount + 1);
};

afterEach(() => {
nodeH2Handler.destroy();
});
Expand All @@ -76,94 +95,62 @@ describe(NodeHttp2Handler.name, () => {

it("is one when request is made", async () => {
// Make single request.
const response = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});
const responseBody = response.response.body as ClientHttp2Stream;
const { response } = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});

const authority = `${protocol}//${hostname}:${port}`;
expect(connectSpy).toHaveBeenCalledTimes(1);
expect(connectSpy).toHaveBeenCalledWith(authority);

// Keeping node alive while request is open.
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(1);

const closed = new Promise<void>((resolve) => responseBody.once("close", resolve));
(response.response.body as ClientHttp2Stream).destroy();
await closed;

// No longer keeping node alive
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(2);
expectSessionCreatedAndReferred(createdSessions[0]);
await closeConnection(response);
expectSessionCreatedAndUnreffed(createdSessions[0]);
});

it("is one if multiple requests are made on same URL", async () => {
const connectSpy = jest.spyOn(http2, "connect");

// Make two requests.
const response1 = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});
const response1Body = response1.response.body as ClientHttp2Stream;
const response2 = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});
const response2Body = response2.response.body as ClientHttp2Stream;
const { response: response1 } = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});
const { response: response2 } = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});

const authority = `${protocol}//${hostname}:${port}`;
expect(connectSpy).toHaveBeenCalledTimes(1);
expect(connectSpy).toHaveBeenCalledWith(authority);

// Keeping node alive while requests are open.
expect(createdSessions[0].ref).toHaveBeenCalledTimes(2);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(1);

const closed1 = new Promise<void>((resolve) => response1Body.once("close", resolve));
response1Body.destroy();
await closed1;
const closed2 = new Promise<void>((resolve) => response2Body.once("close", resolve));
response2Body.destroy();
await closed2;

// No longer keeping node alive
expect(createdSessions[0].ref).toHaveBeenCalledTimes(2);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(3);
expectSessionCreatedAndReferred(createdSessions[0], 2);
await closeConnection(response1);
await closeConnection(response2);
expectSessionCreatedAndUnreffed(createdSessions[0], 2);
});

it("is many if requests are made on different URLs", async () => {
const connectSpy = jest.spyOn(http2, "connect");

// Make first request on default URL.
const response1 = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});
const response1Body = response1.response.body as ClientHttp2Stream;
const { response: response1 } = await nodeH2Handler.handle(new HttpRequest(getMockReqOptions()), {});

const port2 = port + 1;
const mockH2Server2 = createMockHttp2Server().listen(port2);
mockH2Server2.on("request", createResponseFunction(mockResponse));

// Make second request on URL with port2.
const response2 = await nodeH2Handler.handle(new HttpRequest({ ...getMockReqOptions(), port: port2 }), {});
const response2Body = response2.response.body as ClientHttp2Stream;
const { response: response2 } = await nodeH2Handler.handle(
new HttpRequest({ ...getMockReqOptions(), port: port2 }),
{}
);

const authorityPrefix = `${protocol}//${hostname}`;
expect(connectSpy).toHaveBeenCalledTimes(2);
expect(connectSpy).toHaveBeenNthCalledWith(1, `${authorityPrefix}:${port}`);
expect(connectSpy).toHaveBeenNthCalledWith(2, `${authorityPrefix}:${port2}`);
mockH2Server2.close();

// Keeping node alive while requests are open.
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(1);
expect(createdSessions[1].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[1].unref).toHaveBeenCalledTimes(1);

const closed1 = new Promise<void>((resolve) => response1Body.once("close", resolve));
response1Body.destroy();
await closed1;
const closed2 = new Promise<void>((resolve) => response2Body.once("close", resolve));
response2Body.destroy();
await closed2;

// No longer keeping node alive
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(2);
expect(createdSessions[1].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[1].unref).toHaveBeenCalledTimes(2);
expectSessionCreatedAndReferred(createdSessions[0]);
expectSessionCreatedAndReferred(createdSessions[1]);
await closeConnection(response1);
await closeConnection(response2);
expectSessionCreatedAndUnreffed(createdSessions[0]);
expectSessionCreatedAndUnreffed(createdSessions[1]);
});
});

Expand Down Expand Up @@ -214,12 +201,9 @@ describe(NodeHttp2Handler.name, () => {

// Not keeping node alive
expect(createdSessions).toHaveLength(3);
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(2);
expect(createdSessions[1].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[1].unref).toHaveBeenCalledTimes(2);
expect(createdSessions[2].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[2].unref).toHaveBeenCalledTimes(2);
expectSessionCreatedAndUnreffed(createdSessions[0]);
expectSessionCreatedAndUnreffed(createdSessions[1]);
expectSessionCreatedAndUnreffed(createdSessions[2]);

// should be able to recover from goaway after reconnecting to a server
// that doesn't send goaway, and reuse the TCP connection (Http2Session)
Expand All @@ -230,8 +214,7 @@ describe(NodeHttp2Handler.name, () => {

// Keeping node alive
expect(createdSessions).toHaveLength(4);
expect(createdSessions[3].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[3].unref).toHaveBeenCalledTimes(1);
expectSessionCreatedAndReferred(createdSessions[3]);

// ...and validate that the mocked response is received
const responseBody = await new Promise((resolve) => {
Expand All @@ -248,8 +231,7 @@ describe(NodeHttp2Handler.name, () => {

// Not keeping node alive
expect(createdSessions).toHaveLength(4);
expect(createdSessions[3].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[3].unref).toHaveBeenCalledTimes(2);
expectSessionCreatedAndUnreffed(createdSessions[3]);
});

it("handles connections destroyed by servers", async () => {
Expand Down Expand Up @@ -294,12 +276,9 @@ describe(NodeHttp2Handler.name, () => {

// Not keeping node alive
expect(createdSessions).toHaveLength(3);
expect(createdSessions[0].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[0].unref).toHaveBeenCalledTimes(2);
expect(createdSessions[1].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[1].unref).toHaveBeenCalledTimes(2);
expect(createdSessions[2].ref).toHaveBeenCalledTimes(1);
expect(createdSessions[2].unref).toHaveBeenCalledTimes(2);
expectSessionCreatedAndUnreffed(createdSessions[0]);
expectSessionCreatedAndUnreffed(createdSessions[1]);
expectSessionCreatedAndUnreffed(createdSessions[2]);
});
});

Expand Down

0 comments on commit 3f1190c

Please sign in to comment.