Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 3 additions & 27 deletions packages/testcontainers/src/wait-strategies/http-wait-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Dockerode from "dockerode";
import { Agent, Dispatcher, request } from "undici";
import { Agent, request } from "undici";
import { IntervalRetry, log } from "../common";
import { getContainerRuntimeClient } from "../container-runtime";
import { BoundPorts } from "../utils/bound-ports";
import { undiciResponseToFetchResponse } from "./utils/undici-response-parser";
import { AbstractWaitStrategy } from "./wait-strategy";

export interface HttpWaitStrategyOptions {
Expand Down Expand Up @@ -95,7 +96,7 @@ export class HttpWaitStrategy extends AbstractWaitStrategy {
}
}

return this.undiciResponseToFetchResponse(
return undiciResponseToFetchResponse(
await request(url, {
method: this.method,
signal: AbortSignal.timeout(this.readTimeoutMs),
Expand Down Expand Up @@ -164,31 +165,6 @@ export class HttpWaitStrategy extends AbstractWaitStrategy {
throw new Error(message);
}

/**
* Converts an undici response to a fetch response.
* This is necessary because node's fetch does not support disabling SSL validation (https://github.com/orgs/nodejs/discussions/44038).
*
* @param undiciResponse The undici response to convert.
* @returns The fetch response.
*/
private undiciResponseToFetchResponse(undiciResponse: Dispatcher.ResponseData): Response {
const headers = new Headers();
for (const [key, value] of Object.entries(undiciResponse.headers)) {
if (Array.isArray(value)) {
for (const v of value) {
headers.append(key, v);
}
} else if (value !== undefined) {
headers.set(key, value);
}
}

return new Response(undiciResponse.body, {
status: undiciResponse.statusCode,
headers,
});
}

private getAgent(): Agent | undefined {
if (this._allowInsecure) {
return new Agent({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Readable } from "stream";
import { Dispatcher } from "undici";
import BodyReadable from "undici/types/readable";
import { undiciResponseToFetchResponse } from "./undici-response-parser";

test("converts undici response to fetch response", async () => {
const responseData: Partial<Dispatcher.ResponseData> = {
statusCode: 200,
headers: { "content-type": "application/json" },
body: createBody('{"key":"value"}'),
};

const response = undiciResponseToFetchResponse(responseData as Dispatcher.ResponseData);

expect(response.status).toBe(200);
expect(response.headers.get("content-type")).toBe("application/json");
await expect(response.text()).resolves.toBe('{"key":"value"}');
});

test("handles empty headers", async () => {
const responseData: Partial<Dispatcher.ResponseData> = {
statusCode: 200,
body: createBody(),
};

const response = undiciResponseToFetchResponse(responseData as Dispatcher.ResponseData);

expect(response.headers).toEqual(new Headers());
});

test("handles a header array", async () => {
const responseData: Partial<Dispatcher.ResponseData> = {
statusCode: 200,
headers: {
"x-multiple-values": ["value1", "value2"],
},
body: createBody(),
};

const response = undiciResponseToFetchResponse(responseData as Dispatcher.ResponseData);

expect(response.headers.get("x-multiple-values")).toBe("value1, value2");
});

test.each([204, 205, 304])("sets body to null for %i status code", async (statusCode) => {
const responseData: Partial<Dispatcher.ResponseData> = {
statusCode: statusCode,
headers: { "content-type": "application/json" },
body: createBody('{"key":"value"}'),
};

const response = undiciResponseToFetchResponse(responseData as Dispatcher.ResponseData);

await expect(response.text()).resolves.toBe("");
});

function createBody(str: string = ""): BodyReadable {
return Readable.from(Buffer.from(str, "utf-8")) as Dispatcher.ResponseData["body"];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Readable } from "stream";
import { Dispatcher } from "undici";

/**
* Converts an undici response to a fetch response.
* This is necessary because node's fetch does not support disabling SSL validation (https://github.com/orgs/nodejs/discussions/44038).
*
* @param undiciResponse The undici response to convert.
* @returns The fetch response.
*/
export function undiciResponseToFetchResponse(undiciResponse: Dispatcher.ResponseData): Response {
const headers = new Headers();
if (undiciResponse.headers) {
for (const [key, value] of Object.entries(undiciResponse.headers)) {
if (Array.isArray(value)) {
for (const v of value) {
headers.append(key, v);
}
} else if (value !== undefined) {
headers.set(key, value);
}
}
}

const status = undiciResponse.statusCode;
const hasNullBody = status === 204 || status === 205 || status === 304;
const responseBody = hasNullBody ? null : Readable.toWeb(undiciResponse.body);

return new Response(responseBody, { status, headers });
}