From cf22822583252040d980e0387d14ae5c4ca36f2c Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Sat, 7 May 2022 17:41:47 +0100 Subject: [PATCH] fix: don't crash when tail event is null Sometime the "event" on a tail can be null. This patch makes sure we don't crash when that happens. Fixes https://github.com/cloudflare/wrangler2/issues/918 --- .changeset/khaki-starfishes-draw.md | 7 +++ packages/wrangler/src/__tests__/tail.test.ts | 58 +++++++++++++++----- packages/wrangler/src/tail/index.ts | 2 +- packages/wrangler/src/tail/printing.ts | 14 +++-- 4 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 .changeset/khaki-starfishes-draw.md diff --git a/.changeset/khaki-starfishes-draw.md b/.changeset/khaki-starfishes-draw.md new file mode 100644 index 000000000000..a61203a15d68 --- /dev/null +++ b/.changeset/khaki-starfishes-draw.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +fix: don't crash when tail event is null + +Sometime the "event" on a tail can be null. This patch makes sure we don't crash when that happens. Fixes https://github.com/cloudflare/wrangler2/issues/918 diff --git a/packages/wrangler/src/__tests__/tail.test.ts b/packages/wrangler/src/__tests__/tail.test.ts index df229ed163c2..504d3add86df 100644 --- a/packages/wrangler/src/__tests__/tail.test.ts +++ b/packages/wrangler/src/__tests__/tail.test.ts @@ -295,6 +295,31 @@ describe("tail", () => { `); }); + it("should not crash when the tail message has a void event", async () => { + const api = mockWebsocketAPIs(); + await runWrangler("tail test-worker --format pretty"); + + const message = generateMockEventMessage({ event: null }); + const serializedMessage = serialize(message); + + api.ws.send(serializedMessage); + expect( + std.out + .replace( + new Date(mockEventTimestamp).toLocaleString(), + "[mock timestamp string]" + ) + .replace( + mockTailExpiration.toLocaleString(), + "[mock expiration date]" + ) + ).toMatchInlineSnapshot(` + "Successfully created tail, expires at [mock expiration date] + Connected to test-worker, waiting for logs... + [missing request] - Ok @ [mock timestamp string]" + `); + }); + it("defaults to logging in pretty format when the output is a TTY", async () => { setIsTTY(true); const api = mockWebsocketAPIs(); @@ -414,7 +439,8 @@ function serialize(message: TailEventMessage): WebSocket.RawData { // This isn't a problem outside of testing since deserialization // works just fine and wrangler never _sends_ any event messages, // it only receives them. - const request = (message.event as RequestEvent).request; + const request = ((message.event as RequestEvent | undefined | null) || {}) + .request; const stringified = JSON.stringify(message, (key, value) => { if (key !== "request") { return value; @@ -422,9 +448,9 @@ function serialize(message: TailEventMessage): WebSocket.RawData { return { ...request, - url: request.url, - headers: request.headers, - method: request.method, + url: request?.url, + headers: request?.headers, + method: request?.method, }; }); @@ -439,9 +465,9 @@ function serialize(message: TailEventMessage): WebSocket.RawData { * @returns whether event is a ScheduledEvent (true) or a RequestEvent */ function isScheduled( - event: ScheduledEvent | RequestEvent + event: ScheduledEvent | RequestEvent | undefined | null ): event is ScheduledEvent { - return "cron" in event; + return Boolean(event && "cron" in event); } /** @@ -607,15 +633,19 @@ function mockWebsocketAPIs(env?: string, legacyEnv = false): MockAPI { * @param opts Any specific parts of the message to use instead of defaults * @returns a `TailEventMessage` that wrangler can process and display */ -function generateMockEventMessage( - opts?: Partial -): TailEventMessage { +function generateMockEventMessage({ + outcome = "ok", + exceptions = [], + logs = [], + eventTimestamp = mockEventTimestamp, + event = generateMockRequestEvent(), +}: Partial): TailEventMessage { return { - outcome: opts?.outcome || "ok", - exceptions: opts?.exceptions || [], - logs: opts?.logs || [], - eventTimestamp: opts?.eventTimestamp || mockEventTimestamp, - event: opts?.event || generateMockRequestEvent(), + outcome, + exceptions, + logs, + eventTimestamp, + event, }; } diff --git a/packages/wrangler/src/tail/index.ts b/packages/wrangler/src/tail/index.ts index 02abe833dc79..45e7200abb97 100644 --- a/packages/wrangler/src/tail/index.ts +++ b/packages/wrangler/src/tail/index.ts @@ -179,7 +179,7 @@ export type TailEventMessage = { * Until workers-types exposes individual types for export, we'll have * to just re-define these types ourselves. */ - event: RequestEvent | ScheduledEvent; + event: RequestEvent | ScheduledEvent | undefined | null; }; /** diff --git a/packages/wrangler/src/tail/printing.ts b/packages/wrangler/src/tail/printing.ts index 1db9ce684007..589d52e852c6 100644 --- a/packages/wrangler/src/tail/printing.ts +++ b/packages/wrangler/src/tail/printing.ts @@ -15,12 +15,16 @@ export function prettyPrintLogs(data: WebSocket.RawData): void { logger.log(`"${cronPattern}" @ ${datetime} - ${outcome}`); } else { - const requestMethod = eventMessage.event.request.method.toUpperCase(); - const url = eventMessage.event.request.url; + const requestMethod = eventMessage.event?.request.method.toUpperCase(); + const url = eventMessage.event?.request.url; const outcome = prettifyOutcome(eventMessage.outcome); const datetime = new Date(eventMessage.eventTimestamp).toLocaleString(); - logger.log(`${requestMethod} ${url} - ${outcome} @ ${datetime}`); + logger.log( + url + ? `${requestMethod} ${url} - ${outcome} @ ${datetime}` + : `[missing request] - ${outcome} @ ${datetime}` + ); } if (eventMessage.logs.length > 0) { @@ -41,9 +45,9 @@ export function jsonPrintLogs(data: WebSocket.RawData): void { } function isScheduledEvent( - event: RequestEvent | ScheduledEvent + event: RequestEvent | ScheduledEvent | undefined | null ): event is ScheduledEvent { - return "cron" in event; + return Boolean(event && "cron" in event); } function prettifyOutcome(outcome: Outcome): string {