Skip to content

Commit

Permalink
fix: export createNodeHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak committed Nov 25, 2023
1 parent 8423803 commit 7bb7de6
Show file tree
Hide file tree
Showing 5 changed files with 575 additions and 95 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
} from "./types.js";

export { createNodeMiddleware } from "./middleware/node/index.js";
export { createNodeHandler } from "./middleware/node/handler.js";
export { emitterEventNames } from "./generated/webhook-names.js";

// U holds the return value of `transform` function in Options
Expand Down
5 changes: 0 additions & 5 deletions src/middleware/node/get-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import AggregateError from "aggregate-error";
type IncomingMessage = any;

export function getPayload(request: IncomingMessage): Promise<string> {
// If request.body already exists we can stop here
// See https://github.com/octokit/webhooks.js/pull/23

if (request.body) return Promise.resolve(request.body);

return new Promise((resolve, reject) => {
let data = "";

Expand Down
118 changes: 118 additions & 0 deletions src/middleware/node/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// remove type imports from http for Deno compatibility
// see https://github.com/octokit/octokit.js/issues/2075#issuecomment-817361886
// import { IncomingMessage, ServerResponse } from "http";
type IncomingMessage = any;
type ServerResponse = any;

import type { WebhookEventName } from "@octokit/webhooks-types";

import type { Webhooks } from "../../index.js";
import type { WebhookEventHandlerError } from "../../types.js";
import type { MiddlewareOptions } from "./types.js";
import { getMissingHeaders } from "./get-missing-headers.js";
import { getPayload } from "./get-payload.js";

type Handler = (
request: IncomingMessage,
response: ServerResponse,
) => Promise<boolean>;
export function createNodeHandler(
webhooks: Webhooks,
options?: Pick<MiddlewareOptions, "log">,
): Handler {
const logger = options?.log || console;
return async function handler(
request: IncomingMessage,
response: ServerResponse,
): Promise<boolean> {
// Check if the Content-Type header is `application/json` and allow for charset to be specified in it
// Otherwise, return a 415 Unsupported Media Type error
// See https://github.com/octokit/webhooks.js/issues/158
if (
!request.headers["content-type"] ||
!request.headers["content-type"].startsWith("application/json")
) {
response.writeHead(415, {
"content-type": "application/json",
accept: "application/json",
});
response.end(
JSON.stringify({
error: `Unsupported "Content-Type" header value. Must be "application/json"`,
}),
);
return true;
}

const missingHeaders = getMissingHeaders(request).join(", ");

if (missingHeaders) {
response.writeHead(400, {
"content-type": "application/json",
});
response.end(
JSON.stringify({
error: `Required headers missing: ${missingHeaders}`,
}),
);

return true;
}

const eventName = request.headers["x-github-event"] as WebhookEventName;
const signatureSHA256 = request.headers["x-hub-signature-256"] as string;
const id = request.headers["x-github-delivery"] as string;

logger.debug(`${eventName} event received (id: ${id})`);

// GitHub will abort the request if it does not receive a response within 10s
// See https://github.com/octokit/webhooks.js/issues/185
let didTimeout = false;
const timeout = setTimeout(() => {
didTimeout = true;
response.statusCode = 202;
response.end("still processing\n");
}, 9000).unref();

try {
// If request.body already exists we dont need to wait for getPayload
// See https://github.com/octokit/webhooks.js/pull/23

const payload = request.body || (await getPayload(request));

await webhooks.verifyAndReceive({
id: id,
name: eventName as any,
payload,
signature: signatureSHA256,
});
clearTimeout(timeout);

if (didTimeout) return true;

response.end("ok\n");
return true;
} catch (error) {
clearTimeout(timeout);

if (didTimeout) return true;

const err = Array.from(error as WebhookEventHandlerError)[0];
const errorMessage = err.message
? `${err.name}: ${err.message}`
: "Error: An Unspecified error occurred";
response.statusCode =
typeof err.status !== "undefined" ? err.status : 500;

logger.error(error);

response.end(
JSON.stringify({
error: errorMessage,
}),
);

return true;
}
};
}
92 changes: 2 additions & 90 deletions src/middleware/node/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@
type IncomingMessage = any;
type ServerResponse = any;

import type { WebhookEventName } from "@octokit/webhooks-types";

import type { Webhooks } from "../../index.js";
import type { WebhookEventHandlerError } from "../../types.js";
import type { MiddlewareOptions } from "./types.js";
import { getMissingHeaders } from "./get-missing-headers.js";
import { getPayload } from "./get-payload.js";
import { onUnhandledRequestDefault } from "./on-unhandled-request-default.js";
import { createNodeHandler } from "./handler.js";

export async function middleware(
webhooks: Webhooks,
Expand Down Expand Up @@ -43,89 +39,5 @@ export async function middleware(
return true;
}

// Check if the Content-Type header is `application/json` and allow for charset to be specified in it
// Otherwise, return a 415 Unsupported Media Type error
// See https://github.com/octokit/webhooks.js/issues/158
if (
!request.headers["content-type"] ||
!request.headers["content-type"].startsWith("application/json")
) {
response.writeHead(415, {
"content-type": "application/json",
accept: "application/json",
});
response.end(
JSON.stringify({
error: `Unsupported "Content-Type" header value. Must be "application/json"`,
}),
);
return true;
}

const missingHeaders = getMissingHeaders(request).join(", ");

if (missingHeaders) {
response.writeHead(400, {
"content-type": "application/json",
});
response.end(
JSON.stringify({
error: `Required headers missing: ${missingHeaders}`,
}),
);

return true;
}

const eventName = request.headers["x-github-event"] as WebhookEventName;
const signatureSHA256 = request.headers["x-hub-signature-256"] as string;
const id = request.headers["x-github-delivery"] as string;

options.log.debug(`${eventName} event received (id: ${id})`);

// GitHub will abort the request if it does not receive a response within 10s
// See https://github.com/octokit/webhooks.js/issues/185
let didTimeout = false;
const timeout = setTimeout(() => {
didTimeout = true;
response.statusCode = 202;
response.end("still processing\n");
}, 9000).unref();

try {
const payload = await getPayload(request);

await webhooks.verifyAndReceive({
id: id,
name: eventName as any,
payload,
signature: signatureSHA256,
});
clearTimeout(timeout);

if (didTimeout) return true;

response.end("ok\n");
return true;
} catch (error) {
clearTimeout(timeout);

if (didTimeout) return true;

const err = Array.from(error as WebhookEventHandlerError)[0];
const errorMessage = err.message
? `${err.name}: ${err.message}`
: "Error: An Unspecified error occurred";
response.statusCode = typeof err.status !== "undefined" ? err.status : 500;

options.log.error(error);

response.end(
JSON.stringify({
error: errorMessage,
}),
);

return true;
}
return createNodeHandler(webhooks, options)(request, response);
}
Loading

0 comments on commit 7bb7de6

Please sign in to comment.