From f86a8259f37f0ed25b00e243b29aa28c3e68bdff Mon Sep 17 00:00:00 2001 From: neatchee Date: Thu, 17 Dec 2020 08:49:43 -0800 Subject: [PATCH] feat(lookup): handle Cloudflare DDoS protection (#1434) Resolves #1297 --- src/logger.ts | 12 +++++++ src/store/lookup.ts | 78 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 108645b18a..b04dffb6ab 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -95,6 +95,18 @@ export const Print = { return `✖ ${buildProductString(link, store)} :: CAPTCHA`; }, + cloudflare(link: Link, store: Store, color?: boolean): string { + if (color) { + return ( + '✖ ' + + buildProductString(link, store, true) + + ' :: ' + + chalk.yellow('CLOUDFLARE, WAITING') + ); + } + + return `✖ ${buildProductString(link, store)} :: CLOUDFLARE, WAITING`; + }, inStock(link: Link, store: Store, color?: boolean, sms?: boolean): string { const productString = `${buildProductString(link, store)} :: IN STOCK`; diff --git a/src/store/lookup.ts b/src/store/lookup.ts index f95838f065..bcacc4f5b4 100644 --- a/src/store/lookup.ts +++ b/src/store/lookup.ts @@ -275,19 +275,16 @@ async function lookupCard( waitUntil: givenWaitFor }); - if (!response) { - logger.debug(Print.noResponse(link, store, true)); - } - const successStatusCodes = store.successStatusCodes ?? [[0, 399]]; - const statusCode = response?.status() ?? 0; - if (!isStatusCodeInRange(statusCode, successStatusCodes)) { - if (statusCode === 429) { - logger.warn(Print.rateLimit(link, store, true)); - } else { - logger.warn(Print.badStatusCode(link, store, statusCode, true)); - } + const statusCode = await handleResponse( + browser, + store, + page, + link, + response + ); + if (!isStatusCodeInRange(statusCode, successStatusCodes)) { return statusCode; } @@ -325,6 +322,65 @@ async function lookupCard( return statusCode; } +async function handleResponse( + browser: Browser, + store: Store, + page: Page, + link: Link, + response?: Response | null +) { + if (!response) { + logger.debug(Print.noResponse(link, store, true)); + } + + const successStatusCodes = store.successStatusCodes ?? [[0, 399]]; + let statusCode = response?.status() ?? 0; + if (!isStatusCodeInRange(statusCode, successStatusCodes)) { + if (statusCode === 429) { + logger.warn(Print.rateLimit(link, store, true)); + } else if (statusCode === 503) { + if (await checkIsCloudflare(store, page, link)) { + const response: Response | null = await page.waitForNavigation({ + waitUntil: 'networkidle0' + }); + statusCode = await handleResponse( + browser, + store, + page, + link, + response + ); + } else { + logger.warn(Print.badStatusCode(link, store, statusCode, true)); + } + } else { + logger.warn(Print.badStatusCode(link, store, statusCode, true)); + } + } + + return statusCode; +} + +async function checkIsCloudflare(store: Store, page: Page, link: Link) { + const baseOptions: Selector = { + requireVisible: true, + selector: 'body', + type: 'textContent' + }; + + const cloudflareLabel = { + container: 'div[class="attribution"] a[rel="noopener noreferrer"]', + text: ['Cloudflare'] + }; + + if (await pageIncludesLabels(page, cloudflareLabel, baseOptions)) { + logger.warn(Print.cloudflare(link, store, true)); + return true; + } + + return false; +} + async function lookupCardInStock(store: Store, page: Page, link: Link) { const baseOptions: Selector = { requireVisible: false,