From cd8f090a5edb6860fa18090e70cbb5910f60aecb Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Wed, 26 Jul 2023 12:38:00 +0100 Subject: [PATCH] add HTTP Head request --- .vscode/launch.json | 16 +--- Common/Types/API/HTTPMethod.ts | 1 + Common/Types/WebsiteRequest.ts | 27 +++++-- Probe/Utils/Monitors/Monitor.ts | 49 +++++++++++- .../Utils/Monitors/MonitorTypes/ApiMonitor.ts | 43 ++++++---- .../Monitors/MonitorTypes/WebsiteMonitor.ts | 79 ++++++++++++++----- 6 files changed, 159 insertions(+), 56 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 65e8d2aa8c..e57a1cc00c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -86,7 +86,7 @@ { "address": "127.0.0.1", "localRoot": "${workspaceFolder}/Probe", - "name": "Dashboard API: Debug with Docker", + "name": "Probe: Debug with Docker", "port": 9655, "remoteRoot": "/usr/src/app", "request": "attach", @@ -335,20 +335,6 @@ "restart": true, "autoAttachChildProcesses": true }, - { - "address": "127.0.0.1", - "localRoot": "${workspaceFolder}/probe", - "name": "Probe: Debug with Docker", - "port": 9238, - "remoteRoot": "/usr/src/app", - "request": "attach", - "skipFiles": [ - "/**" - ], - "type": "node", - "restart": true, - "autoAttachChildProcesses": true - }, { "name": "CommonServer: Debug Tests", "type": "node", diff --git a/Common/Types/API/HTTPMethod.ts b/Common/Types/API/HTTPMethod.ts index 952e45b05d..4318878ea6 100644 --- a/Common/Types/API/HTTPMethod.ts +++ b/Common/Types/API/HTTPMethod.ts @@ -3,6 +3,7 @@ enum HTTPMethod { POST = 'POST', DELETE = 'DELETE', PUT = 'PUT', + HEAD = 'HEAD', } export default HTTPMethod; diff --git a/Common/Types/WebsiteRequest.ts b/Common/Types/WebsiteRequest.ts index b5b011a1ca..4862c75e16 100644 --- a/Common/Types/WebsiteRequest.ts +++ b/Common/Types/WebsiteRequest.ts @@ -1,7 +1,8 @@ -import axios, { AxiosResponse } from 'axios'; +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import Headers from './API/Headers'; import URL from './API/URL'; import HTML from './Html'; +import HTTPMethod from './API/HTTPMethod'; export interface WebsiteResponse { url: URL; @@ -13,18 +14,32 @@ export interface WebsiteResponse { } export default class WebsiteRequest { - public static async get( + public static async fetch( url: URL, options: { headers?: Headers | undefined; timeout?: number | undefined; + isHeadRequest?: boolean | undefined; } ): Promise { - // use axios to fetch an HTML page - const response: AxiosResponse = await axios.get(url.toString(), { - headers: options.headers || {}, + const axiosOptions: AxiosRequestConfig = { timeout: options.timeout || 5000, - }); + method: HTTPMethod.GET, + }; + + if (options.headers) { + axiosOptions.headers = options.headers; + } + + if (options.isHeadRequest) { + axiosOptions.method = HTTPMethod.HEAD; + } + + // use axios to fetch an HTML page + const response: AxiosResponse = await axios( + url.toString(), + axiosOptions + ); // return the response return { diff --git a/Probe/Utils/Monitors/Monitor.ts b/Probe/Utils/Monitors/Monitor.ts index f55b38fa46..726c848fee 100644 --- a/Probe/Utils/Monitors/Monitor.ts +++ b/Probe/Utils/Monitors/Monitor.ts @@ -16,6 +16,8 @@ import ApiMonitor, { APIResponse } from './MonitorTypes/ApiMonitor'; import JSONFunctions from 'Common/Types/JSONFunctions'; import logger from 'CommonServer/Utils/Logger'; import ProbeUtil from '../Probe'; +import MonitorCriteriaInstance from 'Common/Types/Monitor/MonitorCriteriaInstance'; +import { CheckOn, CriteriaFilter } from 'Common/Types/Monitor/CriteriaFilter'; export default class MonitorUtil { public static async probeMonitor( @@ -63,6 +65,47 @@ export default class MonitorUtil { return results; } + public static isHeadRequest(monitorStep: MonitorStep): boolean { + // If its not GET requestm it cannot be a head request + if ( + monitorStep.data?.requestType && + monitorStep.data?.requestType !== HTTPMethod.GET + ) { + return false; + } + + // check if monitor step has any criteria with needs request body. If no, then return true otherwise return false. + + if ( + monitorStep.data?.monitorCriteria.data + ?.monitorCriteriaInstanceArray && + monitorStep.data?.monitorCriteria.data?.monitorCriteriaInstanceArray + .length > 0 + ) { + const criteriaArray: Array = + monitorStep.data?.monitorCriteria.data + ?.monitorCriteriaInstanceArray; + + for (const criteria of criteriaArray) { + if ( + criteria.data?.filters && + criteria.data?.filters.length > 0 + ) { + const filters: Array = + criteria.data?.filters; + + for (const filter of filters) { + if (filter.checkOn === CheckOn.ResponseBody) { + return false; + } + } + } + } + } + + return true; + } + public static async probeMonitorStep( monitorStep: MonitorStep, monitor: Monitor @@ -91,7 +134,10 @@ export default class MonitorUtil { if (monitor.monitorType === MonitorType.Website) { const response: ProbeWebsiteResponse = await WebsiteMonitor.ping( - monitorStep.data?.monitorDestination as URL + monitorStep.data?.monitorDestination as URL, + { + isHeadRequest: MonitorUtil.isHeadRequest(monitorStep), + } ); result.isOnline = response.isOnline; @@ -117,6 +163,7 @@ export default class MonitorUtil { { requestHeaders: monitorStep.data?.requestHeaders || {}, requestBody: requestBody || undefined, + isHeadRequest: MonitorUtil.isHeadRequest(monitorStep), requestType: monitorStep.data?.requestType || HTTPMethod.GET, } diff --git a/Probe/Utils/Monitors/MonitorTypes/ApiMonitor.ts b/Probe/Utils/Monitors/MonitorTypes/ApiMonitor.ts index 6932cc7717..43839a68f9 100644 --- a/Probe/Utils/Monitors/MonitorTypes/ApiMonitor.ts +++ b/Probe/Utils/Monitors/MonitorTypes/ApiMonitor.ts @@ -28,16 +28,25 @@ export default class ApiMonitor { requestHeaders?: Headers | undefined; requestBody?: JSONObject | undefined; requestType?: HTTPMethod | undefined; - }, - retry?: number | undefined + isHeadRequest?: boolean | undefined; + retry?: number | undefined; + } ): Promise { + let requestType: HTTPMethod = options.requestType || HTTPMethod.GET; + + if (options.isHeadRequest) { + requestType = HTTPMethod.HEAD; + } + try { - logger.info(`API Monitor - Pinging ${url.toString()}`); + logger.info( + `API Monitor - Pinging ${requestType} ${url.toString()}` + ); const startTime: [number, number] = process.hrtime(); const result: HTTPResponse | HTTPErrorResponse = await API.fetch( - options.requestType || HTTPMethod.GET, + requestType, url, options.requestBody || undefined, options.requestHeaders || undefined @@ -61,27 +70,23 @@ export default class ApiMonitor { }; logger.info( - `API Monitor - Pinging ${url.toString()} Success - Response: ${JSON.stringify( + `API Monitor - Pinging ${requestType} ${url.toString()} Success - Response: ${JSON.stringify( apiResponse )}` ); return apiResponse; } catch (err) { - logger.error( - `API Monitor - Pinging ${url.toString()} - Error: ${err}` - ); - - if (!retry) { - retry = 0; // default value + if (!options.retry) { + options.retry = 0; // default value } - if (retry < 5) { - retry++; - return await this.ping(url, options, retry); + if (options.retry < 5) { + options.retry++; + return await this.ping(url, options); } - return { + const apiResponse: APIResponse = { url: url, isOnline: false, requestBody: options.requestBody || {}, @@ -92,6 +97,14 @@ export default class ApiMonitor { responseBody: '', responseHeaders: {}, }; + + logger.error( + `API Monitor - Pinging ${requestType} ${url.toString()} - ERROR: ${err} Response: ${JSON.stringify( + apiResponse + )}` + ); + + return apiResponse; } } } diff --git a/Probe/Utils/Monitors/MonitorTypes/WebsiteMonitor.ts b/Probe/Utils/Monitors/MonitorTypes/WebsiteMonitor.ts index 0d61e1c79e..042aab165e 100644 --- a/Probe/Utils/Monitors/MonitorTypes/WebsiteMonitor.ts +++ b/Probe/Utils/Monitors/MonitorTypes/WebsiteMonitor.ts @@ -5,6 +5,8 @@ import Protocol from 'Common/Types/API/Protocol'; import WebsiteRequest, { WebsiteResponse } from 'Common/Types/WebsiteRequest'; import HTML from 'Common/Types/Html'; import { AxiosError } from 'axios'; +import logger from 'CommonServer/Utils/Logger'; +import HTTPMethod from 'Common/Types/API/HTTPMethod'; export interface ProbeWebsiteResponse { url: URL; @@ -20,17 +22,33 @@ export interface ProbeWebsiteResponse { export default class WebsiteMonitor { public static async ping( url: URL, - retry?: number | undefined + options: { + retry?: number | undefined; + isHeadRequest?: boolean | undefined; + } ): Promise { + let requestType: HTTPMethod = HTTPMethod.GET; + + if (options.isHeadRequest) { + requestType = HTTPMethod.HEAD; + } + try { + logger.info( + `Website Monitor - Pinging ${requestType} ${url.toString()}` + ); + const startTime: [number, number] = process.hrtime(); - const result: WebsiteResponse = await WebsiteRequest.get(url, {}); + const result: WebsiteResponse = await WebsiteRequest.fetch(url, { + isHeadRequest: options.isHeadRequest, + }); + const endTime: [number, number] = process.hrtime(startTime); const responseTimeInMS: PositiveNumber = new PositiveNumber( (endTime[0] * 1000000000 + endTime[1]) / 1000000 ); - return { + const probeWebsiteResponse: ProbeWebsiteResponse = { url: url, requestHeaders: {}, isOnline: true, @@ -40,18 +58,33 @@ export default class WebsiteMonitor { responseBody: result.responseBody, responseHeaders: result.responseHeaders, }; + + logger.info( + `Website Monitor - Pinging ${requestType} ${url.toString()} Success - Response: ${JSON.stringify( + probeWebsiteResponse + )}` + ); + + return probeWebsiteResponse; } catch (err) { - if (!retry) { - retry = 0; // default value + if (!options) { + options = {}; } - if (retry < 5) { - retry++; - return await this.ping(url, retry); + if (!options.retry) { + options.retry = 0; // default value } + if (options.retry < 5) { + options.retry++; + return await this.ping(url, options); + } + + let probeWebisteResponse: ProbeWebsiteResponse | undefined = + undefined; + if (err instanceof AxiosError) { - return { + probeWebisteResponse = { url: url, isOnline: Boolean(err.response), requestHeaders: {}, @@ -61,18 +94,26 @@ export default class WebsiteMonitor { responseBody: err.response?.data, responseHeaders: (err.response?.headers as Headers) || {}, }; + } else { + probeWebisteResponse = { + url: url, + isOnline: false, + requestHeaders: {}, + isSecure: url.protocol === Protocol.HTTPS, + responseTimeInMS: new PositiveNumber(0), + statusCode: undefined, + responseBody: undefined, + responseHeaders: undefined, + }; } - return { - url: url, - isOnline: false, - requestHeaders: {}, - isSecure: url.protocol === Protocol.HTTPS, - responseTimeInMS: new PositiveNumber(0), - statusCode: undefined, - responseBody: undefined, - responseHeaders: undefined, - }; + logger.error( + `Website Monitor - Pinging ${requestType} ${url.toString()} - ERROR: ${err} Response: ${JSON.stringify( + probeWebisteResponse + )}` + ); + + return probeWebisteResponse; } } }