Skip to content

Commit 0ce04fe

Browse files
committed
fix: strip square brackets if present from hostname
Refs: nodejs/node#39738
1 parent f713e4b commit 0ce04fe

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

packages/credential-provider-imds/src/remoteProvider/httpRequest.spec.ts

+23-11
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { httpRequest } from "./httpRequest";
77
describe("httpRequest", () => {
88
const requestSpy = jest.spyOn(http, "request");
99
let port: number;
10-
const host = "localhost";
10+
const hostname = "localhost";
1111
const path = "/";
1212

1313
const getOpenPort = async (candidatePort = 4321): Promise<number> => {
@@ -34,9 +34,9 @@ describe("httpRequest", () => {
3434
describe("returns response", () => {
3535
it("defaults to method GET", async () => {
3636
const expectedResponse = "expectedResponse";
37-
const scope = nock(`http://${host}:${port}`).get(path).reply(200, expectedResponse);
37+
const scope = nock(`http://${hostname}:${port}`).get(path).reply(200, expectedResponse);
3838

39-
const response = await httpRequest({ host, path, port });
39+
const response = await httpRequest({ hostname, path, port });
4040
expect(response.toString()).toStrictEqual(expectedResponse);
4141
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);
4242

@@ -46,9 +46,21 @@ describe("httpRequest", () => {
4646
it("uses method passed in options", async () => {
4747
const method = "POST";
4848
const expectedResponse = "expectedResponse";
49-
const scope = nock(`http://${host}:${port}`).post(path).reply(200, expectedResponse);
49+
const scope = nock(`http://${hostname}:${port}`).post(path).reply(200, expectedResponse);
5050

51-
const response = await httpRequest({ host, path, port, method });
51+
const response = await httpRequest({ hostname, path, port, method });
52+
expect(response.toString()).toStrictEqual(expectedResponse);
53+
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);
54+
55+
scope.done();
56+
});
57+
58+
it("works with IPv6 hostname with encapsulated brackets", async () => {
59+
const expectedResponse = "expectedResponse";
60+
const encapsulatedIPv6Hostname = "[::1]";
61+
const scope = nock(`http://${encapsulatedIPv6Hostname}:${port}`).get(path).reply(200, expectedResponse);
62+
63+
const response = await httpRequest({ hostname: encapsulatedIPv6Hostname, path, port });
5264
expect(response.toString()).toStrictEqual(expectedResponse);
5365
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);
5466

@@ -59,9 +71,9 @@ describe("httpRequest", () => {
5971
describe("throws error", () => {
6072
const errorOnStatusCode = async (statusCode: number) => {
6173
it(`statusCode: ${statusCode}`, async () => {
62-
const scope = nock(`http://${host}:${port}`).get(path).reply(statusCode, "continue");
74+
const scope = nock(`http://${hostname}:${port}`).get(path).reply(statusCode, "continue");
6375

64-
await expect(httpRequest({ host, path, port })).rejects.toStrictEqual(
76+
await expect(httpRequest({ hostname, path, port })).rejects.toStrictEqual(
6577
Object.assign(new ProviderError("Error response received from instance metadata service"), { statusCode })
6678
);
6779
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);
@@ -71,9 +83,9 @@ describe("httpRequest", () => {
7183
};
7284

7385
it("when request throws error", async () => {
74-
const scope = nock(`http://${host}:${port}`).get(path).replyWithError("error");
86+
const scope = nock(`http://${hostname}:${port}`).get(path).replyWithError("error");
7587

76-
await expect(httpRequest({ host, path, port })).rejects.toStrictEqual(
88+
await expect(httpRequest({ hostname, path, port })).rejects.toStrictEqual(
7789
new ProviderError("Unable to connect to instance metadata service")
7890
);
7991
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);
@@ -92,12 +104,12 @@ describe("httpRequest", () => {
92104

93105
it("timeout", async () => {
94106
const timeout = 1000;
95-
const scope = nock(`http://${host}:${port}`)
107+
const scope = nock(`http://${hostname}:${port}`)
96108
.get(path)
97109
.delay(timeout * 2)
98110
.reply(200, "expectedResponse");
99111

100-
await expect(httpRequest({ host, path, port, timeout })).rejects.toStrictEqual(
112+
await expect(httpRequest({ hostname, path, port, timeout })).rejects.toStrictEqual(
101113
new ProviderError("TimeoutError from instance metadata service")
102114
);
103115
expect(requestSpy.mock.results[0].value.socket).toHaveProperty("destroyed", true);

packages/credential-provider-imds/src/remoteProvider/httpRequest.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import { IncomingMessage, request, RequestOptions } from "http";
77
*/
88
export function httpRequest(options: RequestOptions): Promise<Buffer> {
99
return new Promise((resolve, reject) => {
10-
const req = request({ method: "GET", ...options });
10+
const req = request({
11+
method: "GET",
12+
...options,
13+
// Node.js http module doesn't accept hostname with square brackets
14+
// Refs: https://github.com/nodejs/node/issues/39738
15+
hostname: options.hostname?.replace(/^\[(.+)\]$/, "$1"),
16+
});
1117

1218
req.on("error", (err) => {
1319
reject(Object.assign(new ProviderError("Unable to connect to instance metadata service"), err));

0 commit comments

Comments
 (0)