namespace _heimdall.check_kinds.http_request { /** */ function parameters_schema( ) : _heimdall.helpers.json_schema.type_schema { return { "type": "object", "additionalProperties": false, "properties": { "request": { "type": "object", "additionalProperties": false, "properties": { "target": { "description": "URL", "type": "string" }, "method": { "type": "string", "enum": [ "GET", "POST" ], "default": "GET" } }, "required": [ "target" ] }, "timeout": { "description": "maximum allowed execution time in seconds", "type": "number", "default": 5.0 }, "follow_redirects": { "description": "whether redirect instructions in responses shall be followend instead of being exposed as result", "type": "boolean", "default": false }, "response": { "type": "object", "additionalProperties": false, "properties": { "status_code": { "description": "checks whether the response status code is this", "anyOf": [ { "type": "null", }, { "type": "integer", }, ], "default": 200 }, "headers": { "description": "conjunctively checks header key-value pairs", "type": "object", "additionalProperties": { "description": "header value", "type": "string" }, "properties": { }, "required": [ ], "default": {} }, "body_part": { "description": "checks whether the response body contains this string", "type": "string" } }, "required": [ ] }, "critical": { "description": "whether a violation of this check shall be leveled as critical instead of concerning", "type": "boolean", "default": true }, "strict": { "deprecated": true, "description": "alias for 'critical'", "type": "boolean", "default": true }, }, "required": [ "request", ] }; } /** */ function normalize_order_node( node : any ) : any { const version : string = ( (! ("critical" in node)) ? "v1" : "v2" ); switch (version) { default: { throw (new Error("unhandled version")); break; } case "v1": { const node_ = lib_plankton.object.patched( { "request": { "method": "GET" }, "timeout": 5.0, "follow_redirects": false, "response": { "status_code": 200 }, "strict": true, }, node, true ); const allowed_methods : Array = ["GET", "POST"]; if (! allowed_methods.includes(node_["request"]["method"])) { throw (new Error("invalid HTTP request method: " + node_["request"]["method"])); } else { return { "request": node_["request"], "timeout": node_["timeout"], "follow_redirects": node_["follow_redirects"], "response": node_["response"], "critical": node_["strict"], }; } break; } case "v2": { const node_ = lib_plankton.object.patched( { "request": { "method": "GET" }, "timeout": 5.0, "follow_redirects": false, "response": { "status_code": 200 }, "critical": true, }, node, true ); const allowed_methods : Array = ["GET", "POST"]; if (! allowed_methods.includes(node_["request"]["method"])) { throw (new Error("invalid HTTP request method: " + node_["request"]["method"])); } else { return node_; } break; } } } /** */ async function run( parameters : { request : { target : string; method : string; }; timeout : float; follow_redirects : boolean; response : { status_code : int; headers ?: Record; body_part ?: string; }; critical : boolean; } ) : Promise<_heimdall.type_result> { let error : (null | Error); const url : URL = new URL(parameters.request.target); const http_request : lib_plankton.http.type_request = { "version": "HTTP/1.1", "scheme": ( (url.protocol.slice(0, -1) === "https") ? "https" : "http" ), "host": url.host, "method": { "GET": lib_plankton.http.enum_method.get, "POST": lib_plankton.http.enum_method.post, }[parameters.request.method], "path": url.pathname, "query": url.search, "headers": { "Host": url.host, }, "body": null, }; let http_response : (null | lib_plankton.http.type_response); try { http_response = await lib_plankton.http.call( http_request, { "timeout": parameters.timeout, "follow_redirects": parameters.follow_redirects, "implementation": "fetch", } ); error = null; } catch (error_) { http_response = null; error = error_; } if (http_response === null) { return { "condition": ( parameters.critical ? _heimdall.enum_condition.critical : _heimdall.enum_condition.concerning ), "info": { "request": parameters.request, "faults": [ lib_plankton.translate.get("checks.http_request.request_failed", {"reason": error.toString()}), ], }, }; } else { let faults : Array = []; // status code { if ( (! ("status_code" in parameters.response)) || (parameters.response.status_code === null) ) { // do nothing } else { const status_code_expected : int = (parameters.response.status_code as int); if (! (http_response.status_code === status_code_expected)) { faults.push( lib_plankton.translate.get( "checks.http_request.status_code_mismatch", { "status_code_actual": http_response.status_code.toFixed(0), "status_code_expected": status_code_expected.toFixed(0), } ) ); } else { // do nothing } } } // headers { if ( (! ("headers" in parameters.response)) || (parameters.response.headers === null) ) { // do nothing } else { const headers_expected : Record = (parameters.response.headers as Record); Object.entries(headers_expected).forEach( ([header_key, header_value]) => { if ( (! (header_key in http_response.headers)) && (! (header_key.toLowerCase() in http_response.headers)) ) { faults.push( lib_plankton.translate.get( "checks.http_request.header_missing", { "key": header_key, "value_expected": header_value, } ) ); } else { if ( (! (http_response.headers[header_key] === header_value)) && (! (http_response.headers[header_key.toLowerCase()].toLowerCase() === header_value.toLowerCase())) ) { faults.push( lib_plankton.translate.get( "checks.http_request.header_value_mismatch", { "key": header_key, "value_actual": http_response.headers[header_key], "value_expected": header_value, } ) ); } else { // do nothing } } } ); } } // body { if ( (! ("body_part" in parameters.response)) || (parameters.response.body_part === null) ) { // do nothing } else { const body_part : string = (parameters.response.body_part as string); if (! http_response.body.toString().includes(body_part)) { faults.push( lib_plankton.translate.get( "checks.http_request.body_misses_part", { "part": body_part, } ) ); } else { // do nothing } } } return { "condition": ( (faults.length <= 0) ? _heimdall.enum_condition.ok : ( parameters.critical ? _heimdall.enum_condition.critical : _heimdall.enum_condition.concerning ) ), "info": { "request": parameters.request, "response": { "status_code": http_response.status_code, "headers": http_response.headers, // "body": http_response.body, }, "faults": faults, } }; } } /** */ export function check_kind_implementation( ) : type_check_kind { return { "parameters_schema": parameters_schema, "normalize_order_node": normalize_order_node, "run": run, }; } }