diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index 3bddf092..ea095649 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -126,6 +126,7 @@ const Categories = [ { name: "Networking", ops: [ + "HTTP request", "Strip HTTP headers", "Parse User Agent", "Parse IP range", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index c7101bb4..0d6778fc 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -3397,7 +3397,49 @@ const OperationConfig = { run: Image.runRemoveEXIF, inputType: "byteArray", outputType: "byteArray", - args: [], + args: [] + }, + "HTTP request": { + description: [ + "Makes an HTTP request and returns the response.", + "

", + "This operation supports different HTTP verbs like GET, POST, PUT, etc.", + "

", + "You can add headers line by line in the format Key: Value", + "

", + "The status code of the response, along with a limited selection of exposed headers, can be viewed by checking the 'Show response metadata' option. Only a limited set of response headers are exposed by the browser for security reasons.", + ].join("\n"), + run: HTTP.runHTTPRequest, + inputType: "string", + outputType: "string", + manualBake: true, + args: [ + { + name: "Method", + type: "option", + value: HTTP.METHODS, + }, + { + name: "URL", + type: "string", + value: "", + }, + { + name: "Headers", + type: "text", + value: "", + }, + { + name: "Mode", + type: "option", + value: HTTP.MODE, + }, + { + name: "Show response metadata", + type: "boolean", + value: false, + } + ] }, }; diff --git a/src/core/operations/HTTP.js b/src/core/operations/HTTP.js index 28c41ad5..c02c3628 100755 --- a/src/core/operations/HTTP.js +++ b/src/core/operations/HTTP.js @@ -12,6 +12,17 @@ import {UAS_parser as UAParser} from "../lib/uas_parser.js"; */ const HTTP = { + /** + * @constant + * @default + */ + METHODS: [ + "GET", "POST", "HEAD", + "PUT", "PATCH", "DELETE", + "CONNECT", "TRACE", "OPTIONS" + ], + + /** * Strip HTTP headers operation. * @@ -51,6 +62,94 @@ const HTTP = { "Device Type: " + ua.deviceType + "\n"; }, + + /** + * @constant + * @default + */ + MODE: [ + "Cross-Origin Resource Sharing", + "No CORS (limited to HEAD, GET or POST)", + ], + + /** + * Lookup table for HTTP modes + * + * @private + * @constant + */ + _modeLookup: { + "Cross-Origin Resource Sharing": "cors", + "No CORS (limited to HEAD, GET or POST)": "no-cors", + }, + + /** + * HTTP request operation. + * + * @author tlwr [toby@toby.codes] + * @author n1474335 [n1474335@gmail.com] + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runHTTPRequest(input, args) { + const method = args[0], + url = args[1], + headersText = args[2], + mode = args[3], + showResponseMetadata = args[4]; + + if (url.length === 0) return ""; + + let headers = new Headers(); + headersText.split(/\r?\n/).forEach(line => { + line = line.trim(); + + if (line.length === 0) return; + + let split = line.split(":"); + if (split.length !== 2) throw `Could not parse header in line: ${line}`; + + headers.set(split[0].trim(), split[1].trim()); + }); + + let config = { + method: method, + headers: headers, + mode: HTTP._modeLookup[mode], + cache: "no-cache", + }; + + if (method !== "GET" && method !== "HEAD") { + config.body = input; + } + + return fetch(url, config) + .then(r => { + if (r.status === 0 && r.type === "opaque") { + return "Error: Null response. Try setting the connection mode to CORS."; + } + + if (showResponseMetadata) { + let headers = ""; + for (let pair of r.headers.entries()) { + headers += " " + pair[0] + ": " + pair[1] + "\n"; + } + return r.text().then(b => { + return "####\n Status: " + r.status + " " + r.statusText + + "\n Exposed headers:\n" + headers + "####\n\n" + b; + }); + } + return r.text(); + }) + .catch(e => { + return e.toString() + + "\n\nThis error could be caused by one of the following:\n" + + " - An invalid URL\n" + + " - Making a cross-origin request to a server which does not support CORS\n"; + }); + }, + }; export default HTTP;