From b2600d3ffdfe245e6f720d7ea1e31f3a51ed45f6 Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Sat, 30 May 2020 18:26:26 -0400 Subject: [PATCH 1/4] Added cancellable requests for AxiosStrategy --- functions/strategies/AxiosStrategy.js | 86 ++++++++++++++++++--------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/functions/strategies/AxiosStrategy.js b/functions/strategies/AxiosStrategy.js index 1deda3333..81361009f 100644 --- a/functions/strategies/AxiosStrategy.js +++ b/functions/strategies/AxiosStrategy.js @@ -1,39 +1,69 @@ import axios from "axios" +let cancelSource = axios.CancelToken.source() + +export const cancelRunningAxiosRequest = () => { + cancelSource.cancel() + + // Create a new cancel token + cancelSource = axios.CancelToken.source() +} + const axiosWithProxy = async (req, { state }) => { - const { data } = await axios.post( - state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/", - req - ) - return data + try { + const { data } = await axios.post( + state.postwoman.settings.PROXY_URL || "https://postwoman.apollosoftware.xyz/", + req, + { + cancelToken: cancelSource.token, + } + ) + return data + } catch (e) { + // Check if the throw is due to a cancellation + if (axios.isCancel(e)) { + throw "cancellation" + } else { + throw e + } + } } const axiosWithoutProxy = async (req, _store) => { - const res = await axios({ - ...req, - transformResponse: [ - (data, headers) => { - // If the response has a JSON content type, try parsing it - if ( - headers["content-type"] && - (headers["content-type"].startsWith("application/json") || - headers["content-type"].startsWith("application/vnd.api+json") || - headers["content-type"].startsWith("application/hal+json")) - ) { - try { - const jsonData = JSON.parse(data) - return jsonData - } catch (e) { - return data + try { + const res = await axios({ + ...req, + cancelToken: cancelSource.token, + transformResponse: [ + (data, headers) => { + // If the response has a JSON content type, try parsing it + if ( + headers["content-type"] && + (headers["content-type"].startsWith("application/json") || + headers["content-type"].startsWith("application/vnd.api+json") || + headers["content-type"].startsWith("application/hal+json")) + ) { + try { + const jsonData = JSON.parse(data) + return jsonData + } catch (e) { + return data + } } - } - // Else return the string itself without any transformations - return data - }, - ], - }) - return res + // Else return the string itself without any transformations + return data + }, + ], + }) + return res + } catch (e) { + if (axios.isCancel(e)) { + throw "cancellation" + } else { + throw e + } + } } const axiosStrategy = (req, store) => { From 62c3b341bb2b07ecf91ab4f843ec558284824a5f Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Sat, 30 May 2020 18:26:51 -0400 Subject: [PATCH 2/4] Added cancellable requests support for ExtensionStrategy --- functions/strategies/ExtensionStrategy.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/functions/strategies/ExtensionStrategy.js b/functions/strategies/ExtensionStrategy.js index bfea02235..2d09c1a88 100644 --- a/functions/strategies/ExtensionStrategy.js +++ b/functions/strategies/ExtensionStrategy.js @@ -7,6 +7,12 @@ export const hasChromeExtensionInstalled = () => export const hasFirefoxExtensionInstalled = () => hasExtensionInstalled() && /Firefox/i.test(navigator.userAgent) +export const cancelRunningExtensionRequest = () => { + if (hasExtensionInstalled() && window.__POSTWOMAN_EXTENSION_HOOK__.cancelRunningRequest) { + window.__POSTWOMAN_EXTENSION_HOOK__.cancelRunningRequest() + } +} + const extensionWithProxy = async (req, { state }) => { const { data } = await window.__POSTWOMAN_EXTENSION_HOOK__.sendRequest({ method: "post", From 0fccd3d5cb7177574aadae3c00cba270eaa5945d Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Sat, 30 May 2020 18:28:13 -0400 Subject: [PATCH 3/4] Added cancel request function for network requests --- functions/network.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/functions/network.js b/functions/network.js index c58cc7fd1..5b45cc31c 100644 --- a/functions/network.js +++ b/functions/network.js @@ -1,5 +1,16 @@ -import AxiosStrategy from "./strategies/AxiosStrategy" -import ExtensionStrategy, { hasExtensionInstalled } from "./strategies/ExtensionStrategy" +import AxiosStrategy, { cancelRunningAxiosRequest } from "./strategies/AxiosStrategy" +import ExtensionStrategy, { + cancelRunningExtensionRequest, + hasExtensionInstalled, +} from "./strategies/ExtensionStrategy" + +export const cancelRunningRequest = (store) => { + if (isExtensionsAllowed(store) && hasExtensionInstalled()) { + cancelRunningExtensionRequest() + } else { + cancelRunningAxiosRequest() + } +} const isExtensionsAllowed = ({ state }) => typeof state.postwoman.settings.EXTENSIONS_ENABLED === "undefined" || @@ -13,7 +24,5 @@ const runAppropriateStrategy = (req, store) => { return AxiosStrategy(req, store) } -const sendNetworkRequest = (req, store) => +export const sendNetworkRequest = (req, store) => runAppropriateStrategy(req, store).finally(() => window.$nuxt.$loading.finish()) - -export { sendNetworkRequest } From 4a2f698ff88203035fb2e51093cfe942a9c85943 Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Sat, 30 May 2020 18:28:46 -0400 Subject: [PATCH 4/4] Added ability to cancel requests --- lang/en-US.json | 1 + pages/index.vue | 128 ++++++++++++++++++++++++++++++------------------ 2 files changed, 81 insertions(+), 48 deletions(-) diff --git a/lang/en-US.json b/lang/en-US.json index 70ab9151f..492cefafe 100644 --- a/lang/en-US.json +++ b/lang/en-US.json @@ -187,6 +187,7 @@ "json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again", "prettify_body": "Prettify body", "cancel": "Cancel", + "cancelled": "Cancelled", "save": "Save", "dismiss": "Dismiss", "are_you_sure": "Are you sure?", diff --git a/pages/index.vue b/pages/index.vue index 84de058f6..f74cfef23 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -189,12 +189,25 @@
  • - + +
  • @@ -1332,7 +1345,7 @@ import runTestScriptWithVariables from "../functions/postwomanTesting" import parseTemplateString from "../functions/templating" import AceEditor from "../components/ui/ace-editor" import { tokenRequest, oauthRedirect } from "../assets/js/oauth" -import { sendNetworkRequest } from "../functions/network" +import { cancelRunningRequest, sendNetworkRequest } from "../functions/network" import { fb } from "../functions/fb" import { getEditorLangForMimeType } from "~/functions/editorutils" import { @@ -1450,6 +1463,7 @@ export default { files: [], filenames: "", navigatorShare: navigator.share, + runningRequest: false, settings: { SCROLL_INTO_ENABLED: @@ -2085,6 +2099,9 @@ export default { } return await sendNetworkRequest(requestOptions, this.$store) }, + cancelRequest() { + cancelRunningRequest(this.$store) + }, async sendRequest() { this.$toast.clear() if (this.settings.SCROLL_INTO_ENABLED) this.scrollInto("response") @@ -2154,12 +2171,16 @@ export default { headers = headersObject try { const startTime = Date.now() + + this.runningRequest = true const payload = await this.makeRequest( auth, headers, requestBody, this.showPreRequestScript && this.preRequestScript ) + this.runningRequest = false + const duration = Date.now() - startTime this.$toast.info(this.$t("finished_in", { duration }), { icon: "done", @@ -2202,55 +2223,66 @@ export default { } })() } catch (error) { - console.log(error) - if (error.response) { - this.response.headers = error.response.headers - this.response.status = error.response.status - this.response.body = error.response.data - // Addition of an entry to the history component. - const entry = { - label: this.requestName, - status: this.response.status, - date: new Date().toLocaleDateString(), - time: new Date().toLocaleTimeString(), - method: this.method, - url: this.url, - path: this.path, - usesScripts: Boolean(this.preRequestScript), - preRequestScript: this.preRequestScript, - } + this.runningRequest = false - if ((this.preRequestScript && this.showPreRequestScript) || hasPathParams(this.params)) { - let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript) - environmentVariables = addPathParamsToVariables(this.params, environmentVariables) - entry.path = parseTemplateString(entry.path, environmentVariables) - entry.url = parseTemplateString(entry.url, environmentVariables) - } - - this.$refs.historyComponent.addEntry(entry) - if (fb.currentUser !== null) { - if (fb.currentSettings[2].value) { - fb.writeHistory(entry) - } - } - return + // If the error is caused by cancellation, do nothing + if (error === "cancellation") { + this.response.status = this.$t("cancelled") + this.response.body = this.$t("cancelled") } else { - this.response.status = error.message - this.response.body = `${error}. ${this.$t("check_console_details")}` - this.$toast.error(`${error} ${this.$t("f12_details")}`, { - icon: "error", - }) - if (!this.$store.state.postwoman.settings.PROXY_ENABLED) { - this.$toast.info(this.$t("enable_proxy"), { - icon: "help", - duration: 8000, - action: { - text: this.$t("yes"), - onClick: (e, toastObject) => { - this.$router.push({ path: "/settings" }) - }, - }, + console.log(error) + if (error.response) { + this.response.headers = error.response.headers + this.response.status = error.response.status + this.response.body = error.response.data + // Addition of an entry to the history component. + const entry = { + label: this.requestName, + status: this.response.status, + date: new Date().toLocaleDateString(), + time: new Date().toLocaleTimeString(), + method: this.method, + url: this.url, + path: this.path, + usesScripts: Boolean(this.preRequestScript), + preRequestScript: this.preRequestScript, + } + + if ( + (this.preRequestScript && this.showPreRequestScript) || + hasPathParams(this.params) + ) { + let environmentVariables = getEnvironmentVariablesFromScript(this.preRequestScript) + environmentVariables = addPathParamsToVariables(this.params, environmentVariables) + entry.path = parseTemplateString(entry.path, environmentVariables) + entry.url = parseTemplateString(entry.url, environmentVariables) + } + + this.$refs.historyComponent.addEntry(entry) + if (fb.currentUser !== null) { + if (fb.currentSettings[2].value) { + fb.writeHistory(entry) + } + } + return + } else { + this.response.status = error.message + this.response.body = `${error}. ${this.$t("check_console_details")}` + this.$toast.error(`${error} ${this.$t("f12_details")}`, { + icon: "error", }) + if (!this.$store.state.postwoman.settings.PROXY_ENABLED) { + this.$toast.info(this.$t("enable_proxy"), { + icon: "help", + duration: 8000, + action: { + text: this.$t("yes"), + onClick: (e, toastObject) => { + this.$router.push({ path: "/settings" }) + }, + }, + }) + } } } }