Skip to content
49 changes: 49 additions & 0 deletions src/apiv2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import * as FormData from "form-data";
import * as auth from "./auth";
import nock from "./test/helpers/nock";
const proxySetup = require("proxy");

Check warning on line 7 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Require statement not part of import statement

Check warning on line 7 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value

import { Client, CLI_OAUTH_PROJECT_NUMBER } from "./apiv2";
import { FirebaseError } from "./error";
Expand All @@ -13,8 +13,8 @@
describe("apiv2", () => {
let authStub: sinon.SinonStub | undefined;
before(() => {
if (typeof (auth.getAccessToken as any).restore !== "function") {

Check warning on line 16 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type

Check warning on line 16 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe member access .restore on an `any` value
authStub = sinon.stub(auth, "getAccessToken").resolves({ access_token: "owner" } as any);

Check warning on line 17 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type

Check warning on line 17 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe argument of type `any` assigned to a parameter of type `Tokens | undefined`
}
});
after(() => {
Expand Down Expand Up @@ -219,6 +219,55 @@
expect(nock.isDone()).to.be.true;
});

it("should not retry when retryOnPrematureClose is false", async () => {
nock("https://example.com").patch("/path/to/foo").once().replyWithError({
message:
"Invalid response body while trying to fetch https://example.com/path/to/foo: Premature close",
code: "ERR_STREAM_PREMATURE_CLOSE",
});
// If a retry fires, this second interceptor would be consumed
const retryScope = nock("https://example.com")
.patch("/path/to/foo")
.once()
.reply(200, { ok: true });

const c = new Client({ urlPrefix: "https://example.com" });
await expect(
c.request({
method: "PATCH",
path: "/path/to/foo",
body: { foo: "bar" },
retries: 1,
retryMinTimeout: 10,
retryMaxTimeout: 15,
retryOnPrematureClose: false,
}),
).to.eventually.be.rejectedWith(FirebaseError);
expect(retryScope.isDone()).to.be.false; // proves no retry occurred
nock.cleanAll();
});

it("should retry a request by default after a premature close error", async () => {
nock("https://example.com").post("/path/to/foo").once().replyWithError({
message:
"Invalid response body while trying to fetch https://example.com/path/to/foo: Premature close",
code: "ERR_STREAM_PREMATURE_CLOSE",
});
nock("https://example.com").post("/path/to/foo").once().reply(200, { ok: true });

const c = new Client({ urlPrefix: "https://example.com" });
const r = await c.request({
method: "POST",
path: "/path/to/foo",
body: { foo: "bar" },
retries: 1,
retryMinTimeout: 10,
retryMaxTimeout: 15,
});
expect(r.status).to.equal(200);
expect(nock.isDone()).to.be.true;
});

it("should not allow resolving on http error when streaming", async () => {
const c = new Client({ urlPrefix: "https://example.com" });
const r = c.request<unknown, NodeJS.ReadableStream>({
Expand Down Expand Up @@ -455,7 +504,7 @@
nock("https://example.com")
.get("/path/to/foo")
.reply(function (this: nock.ReplyFnContext): nock.ReplyFnResult {
expect(this.req.headers["x-goog-user-project"]).is.undefined;

Check warning on line 507 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe member access .req on an `any` value
return [200, { success: true }];
});
const prev = process.env["GOOGLE_CLOUD_QUOTA_PROJECT"];
Expand Down Expand Up @@ -598,7 +647,7 @@
let targetServer: Server;
let oldProxy: string | undefined;
before(async () => {
proxyServer = proxySetup(createServer());

Check warning on line 650 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe call of an `any` typed value

Check warning on line 650 in src/apiv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value
targetServer = createServer((req, res) => {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ proxied: true }));
Expand Down
5 changes: 4 additions & 1 deletion src/apiv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

// Using import would require resolveJsonModule, which seems to break the
// build/output format.
const pkg = require("../package.json");

Check warning on line 19 in src/apiv2.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unsafe assignment of an `any` value
const CLI_VERSION: string = pkg.version;

export const standardHeaders: () => Record<string, string> = () => {
Expand Down Expand Up @@ -94,6 +94,8 @@
retryMinTimeout?: number;
/** Maximum timeout between retries. Defaults to 5s. */
retryMaxTimeout?: number;
/** Set to false to disable the premature-close keep-alive retry for this request. Defaults to true. */
retryOnPrematureClose?: boolean;
}

export type ClientRequestOptions<T> = RequestOptions<T> & ClientVerbOptions;
Expand Down Expand Up @@ -581,7 +583,8 @@
!disabledKeepAlive &&
bodyReplayable &&
!proxyURIFromEnv() &&
isPrematureCloseError(err)
isPrematureCloseError(err) &&
options.retryOnPrematureClose !== false
) {
disabledKeepAlive = true;
fetchOptions.agent = noKeepAliveAgent;
Expand Down
1 change: 1 addition & 0 deletions src/gcp/cloudfunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ export async function updateFunction(
queryParams: {
updateMask: fieldMasks.join(","),
},
retryOnPrematureClose: false,
},
);
return {
Expand Down
2 changes: 1 addition & 1 deletion src/gcp/cloudfunctionsv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export async function updateFunction(cloudFunction: InputCloudFunction): Promise
const res = await client.patch<typeof cloudFunction, Operation>(
cloudFunction.name,
cloudFunction,
{ queryParams },
{ queryParams, retryOnPrematureClose: false },
);
return res.body;
} catch (err: any) {
Expand Down
Loading