feat: add the ability to configure query params encoding for requests (#4412)

Co-authored-by: nivedin <nivedinp@gmail.com>
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Pranay Pandey
2024-10-23 18:44:21 +05:30
committed by GitHub
parent 0e00eeb950
commit cf0978bce0
11 changed files with 193 additions and 114 deletions

View File

@@ -6,7 +6,6 @@ import {
RequestRunResult,
} from "~/services/interceptor.service"
import { Service } from "dioc"
import { cloneDeep } from "lodash-es"
import * as E from "fp-ts/Either"
import { ref, watch } from "vue"
import { z } from "zod"
@@ -24,6 +23,7 @@ import { UIExtensionService } from "~/services/ui-extension.service"
import { x25519 } from "@noble/curves/ed25519"
import { base16 } from "@scure/base"
import { invokeAction } from "~/helpers/actions"
import { preProcessRequest } from "../helpers"
type KeyValuePair = {
key: string
@@ -98,33 +98,6 @@ type RunRequestResponse = {
// and the axios present in this package
type AxiosRequestConfig = Parameters<Interceptor["runRequest"]>[0]
export const preProcessRequest = (
req: AxiosRequestConfig
): AxiosRequestConfig => {
const reqClone = cloneDeep(req)
// If the parameters are URLSearchParams, inject them to URL instead
// This prevents issues of marshalling the URLSearchParams to the proxy
if (reqClone.params instanceof URLSearchParams) {
try {
const url = new URL(reqClone.url ?? "")
for (const [key, value] of reqClone.params.entries()) {
url.searchParams.append(key, value)
}
reqClone.url = url.toString()
} catch (e) {
// making this a non-empty block, so we can make the linter happy.
// we should probably use, allowEmptyCatch, or take the time to do something with the caught errors :)
}
reqClone.params = {}
}
return reqClone
}
async function processBody(
axiosReq: AxiosRequestConfig
): Promise<BodyDef | null> {

View File

@@ -6,34 +6,7 @@ import {
RequestRunResult,
} from "../../../services/interceptor.service"
import axios, { AxiosRequestConfig, CancelToken } from "axios"
import { cloneDeep } from "lodash-es"
export const preProcessRequest = (
req: AxiosRequestConfig
): AxiosRequestConfig => {
const reqClone = cloneDeep(req)
// If the parameters are URLSearchParams, inject them to URL instead
// This prevents issues of marshalling the URLSearchParams to the proxy
if (reqClone.params instanceof URLSearchParams) {
try {
const url = new URL(reqClone.url ?? "")
for (const [key, value] of reqClone.params.entries()) {
url.searchParams.append(key, value)
}
reqClone.url = url.toString()
} catch (e) {
// making this a non-empty block, so we can make the linter happy.
// we should probably use, allowEmptyCatch, or take the time to do something with the caught errors :)
}
reqClone.params = {}
}
return reqClone
}
import { preProcessRequest } from "./helpers"
async function runRequest(
req: AxiosRequestConfig,
@@ -41,11 +14,9 @@ async function runRequest(
): RequestRunResult["response"] {
const timeStart = Date.now()
const processedReq = preProcessRequest(req)
try {
const res = await axios({
...processedReq,
...req,
cancelToken,
responseType: "arraybuffer",
})

View File

@@ -7,13 +7,13 @@ import {
InterceptorError,
RequestRunResult,
} from "~/services/interceptor.service"
import { cloneDeep } from "lodash-es"
import { computed, readonly, ref } from "vue"
import { browserIsChrome, browserIsFirefox } from "~/helpers/utils/userAgent"
import SettingsExtension from "~/components/settings/Extension.vue"
import InterceptorsExtensionSubtitle from "~/components/interceptors/ExtensionSubtitle.vue"
import InterceptorsErrorPlaceholder from "~/components/interceptors/ErrorPlaceholder.vue"
import { until } from "@vueuse/core"
import { preProcessRequest } from "./helpers"
export const defineSubscribableObject = <T extends object>(obj: T) => {
const proxyObject = {
@@ -55,31 +55,6 @@ export const cancelRunningExtensionRequest = () => {
window.__POSTWOMAN_EXTENSION_HOOK__?.cancelRequest()
}
const preProcessRequest = (req: AxiosRequestConfig): AxiosRequestConfig => {
const reqClone = cloneDeep(req)
// If the parameters are URLSearchParams, inject them to URL instead
// This prevents marshalling issues with structured cloning of URLSearchParams
if (reqClone.params instanceof URLSearchParams) {
try {
const url = new URL(reqClone.url ?? "")
for (const [key, value] of reqClone.params.entries()) {
url.searchParams.append(key, value)
}
reqClone.url = url.toString()
} catch (e) {
// making this a non-empty block, so we can make the linter happy.
// we should probably use, allowEmptyCatch, or take the time to do something with the caught errors :)
}
reqClone.params = {}
}
return reqClone
}
export type ExtensionStatus = "available" | "unknown-origin" | "waiting"
/**

View File

@@ -0,0 +1,54 @@
import { AxiosRequestConfig } from "axios"
import { cloneDeep } from "lodash-es"
import { useSetting } from "~/composables/settings"
// Helper function to check if a string is already encoded
const isEncoded = (value: string) => {
try {
return value !== decodeURIComponent(value)
} catch (e) {
return false // in case of malformed URI sequence
}
}
export const preProcessRequest = (
req: AxiosRequestConfig
): AxiosRequestConfig => {
const reqClone = cloneDeep(req)
const encodeMode = useSetting("ENCODE_MODE")
// If the parameters are URLSearchParams, inject them to URL instead
// This prevents issues of marshalling the URLSearchParams to the proxy
if (reqClone.params instanceof URLSearchParams) {
try {
const url = new URL(reqClone.url ?? "")
for (const [key, value] of reqClone.params.entries()) {
let finalValue = value
if (
encodeMode.value === "enable" ||
(encodeMode.value === "auto" &&
/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/.test(value))
) {
// Check if the value is already encoded (e.g., contains % symbols)
if (!isEncoded(value)) {
finalValue = encodeURIComponent(value)
}
}
// Set the parameter with the final value
url.searchParams.append(key, finalValue)
}
// decode the URL to prevent double encoding
reqClone.url = decodeURIComponent(url.toString())
} catch (e) {
// making this a non-empty block, so we can make the linter happy.
// we should probably use, allowEmptyCatch, or take the time to do something with the caught errors :)
}
reqClone.params = {}
}
return reqClone
}

View File

@@ -1,7 +1,7 @@
import { Interceptor, RequestRunResult } from "~/services/interceptor.service"
import { AxiosRequestConfig, CancelToken } from "axios"
import * as E from "fp-ts/Either"
import { preProcessRequest } from "./browser"
import { preProcessRequest } from "./helpers"
import { v4 } from "uuid"
import axios from "axios"
import { settingsStore } from "~/newstore/settings"