feat(relay): control redirect follow (#5508)
Add per-domain toggle to disable automatic HTTP redirect following in the Native and Agent interceptors. When disabled, requests return the redirect response (status code, headers, body) without following the Location header. Previously HTTP redirects were always followed (on browser, can't do much about that, see https://fetch.spec.whatwg.org/#atomic-http-redirect-handling) without option to inspect the redirect response itself. This prevented developers from accessing redirect metadata needed when testing OAuth flows (PKCE where intermediate responses contain authorization tokens), authentication endpoints that return codes in Location headers with 302 status, and debugging API redirect chains. But on the desktop app, redirects were just never followed, creating the opposite effect. The browser's fetch API applies atomic HTTP redirect handling per spec, making it impossible to intercept redirects and inspect their responses. The Native and Agent interceptors use curl and native HTTP clients respectively, both supporting redirect control, making this feature viable for these specific interceptors. (Proxyscotch tbd).
This commit is contained in:
parent
567344a9e3
commit
ecf7d2507a
18 changed files with 254 additions and 18 deletions
6
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
6
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
|
|
@ -1202,7 +1202,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users 0.5.0",
|
"redox_users 0.5.0",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4125,7 +4125,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "relay"
|
name = "relay"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/CuriousCorrelation/relay.git#7270b956321972269eea7cecdccbb2ae839bd0ff"
|
source = "git+https://github.com/CuriousCorrelation/relay.git#ed2329e4ebb71bb984c4705aa950cb9c3f9ff931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"curl",
|
"curl",
|
||||||
|
|
@ -6295,7 +6295,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -1045,6 +1045,7 @@
|
||||||
"validate_certificates": "Validate SSL/TLS Certificates",
|
"validate_certificates": "Validate SSL/TLS Certificates",
|
||||||
"verify_host": "Verify Host",
|
"verify_host": "Verify Host",
|
||||||
"verify_peer": "Verify Peer",
|
"verify_peer": "Verify Peer",
|
||||||
|
"follow_redirects": "Follow Redirects",
|
||||||
"client_certificates": "Client Certificates",
|
"client_certificates": "Client Certificates",
|
||||||
"certificate_settings": "Certificate Settings",
|
"certificate_settings": "Certificate Settings",
|
||||||
"certificate": "Certificate",
|
"certificate": "Certificate",
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,14 @@
|
||||||
{{ t("settings.verify_peer") }}
|
{{ t("settings.verify_peer") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<HoppSmartToggle
|
||||||
|
:on="domainSettings[selectedDomain]?.options?.followRedirects ?? true"
|
||||||
|
@change="toggleFollowRedirects"
|
||||||
|
/>
|
||||||
|
{{ t("settings.follow_redirects") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
:icon="IconFileBadge"
|
:icon="IconFileBadge"
|
||||||
|
|
@ -517,6 +525,10 @@ function updateDomainSettings(newSettings: any) {
|
||||||
...newSettings.security?.certificates,
|
...newSettings.security?.certificates,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
options: {
|
||||||
|
...currentSettings?.options,
|
||||||
|
...newSettings.options,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
store.saveDomainSettings(domain, domainSettings[domain])
|
store.saveDomainSettings(domain, domainSettings[domain])
|
||||||
|
|
@ -538,6 +550,17 @@ function toggleVerifyPeer() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleFollowRedirects() {
|
||||||
|
const currentValue =
|
||||||
|
domainSettings[selectedDomain.value]?.options?.followRedirects ?? true
|
||||||
|
|
||||||
|
updateDomainSettings({
|
||||||
|
options: {
|
||||||
|
followRedirects: !currentValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function toggleProxy() {
|
function toggleProxy() {
|
||||||
updateDomainSettings({
|
updateDomainSettings({
|
||||||
proxy: domainSettings[selectedDomain.value]?.proxy
|
proxy: domainSettings[selectedDomain.value]?.proxy
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,14 @@
|
||||||
{{ t("settings.verify_peer") }}
|
{{ t("settings.verify_peer") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<HoppSmartToggle
|
||||||
|
:on="domainSettings[selectedDomain]?.options?.followRedirects ?? true"
|
||||||
|
@change="toggleFollowRedirects"
|
||||||
|
/>
|
||||||
|
{{ t("settings.follow_redirects") }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-4">
|
<div class="flex space-x-4">
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
:icon="IconFileBadge"
|
:icon="IconFileBadge"
|
||||||
|
|
@ -513,6 +521,10 @@ function updateDomainSettings(newSettings: any) {
|
||||||
...newSettings.security?.certificates,
|
...newSettings.security?.certificates,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
options: {
|
||||||
|
...currentSettings?.options,
|
||||||
|
...newSettings.options,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
store.saveDomainSettings(domain, domainSettings[domain])
|
store.saveDomainSettings(domain, domainSettings[domain])
|
||||||
|
|
@ -534,6 +546,17 @@ function toggleVerifyPeer() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleFollowRedirects() {
|
||||||
|
const currentValue =
|
||||||
|
domainSettings[selectedDomain.value]?.options?.followRedirects ?? true
|
||||||
|
|
||||||
|
updateDomainSettings({
|
||||||
|
options: {
|
||||||
|
followRedirects: !currentValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function toggleProxy() {
|
function toggleProxy() {
|
||||||
updateDomainSettings({
|
updateDomainSettings({
|
||||||
proxy: domainSettings[selectedDomain.value]?.proxy
|
proxy: domainSettings[selectedDomain.value]?.proxy
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,14 @@ export type InputDomainSetting = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
options?: {
|
||||||
|
followRedirects?: boolean
|
||||||
|
maxRedirects?: number
|
||||||
|
timeout?: number
|
||||||
|
decompress?: boolean
|
||||||
|
cookies?: boolean
|
||||||
|
keepAlive?: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertStoreFile = (file: StoreFile): O.Option<Uint8Array> =>
|
const convertStoreFile = (file: StoreFile): O.Option<Uint8Array> =>
|
||||||
|
|
@ -207,19 +215,41 @@ const convertProxy = (
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const convertOptions = (
|
||||||
|
options?: InputDomainSetting["options"]
|
||||||
|
): O.Option<NonNullable<RelayRequest["meta"]>["options"]> =>
|
||||||
|
pipe(
|
||||||
|
O.fromNullable(options),
|
||||||
|
O.map((opts) => ({
|
||||||
|
...(opts.followRedirects !== undefined && {
|
||||||
|
followRedirects: opts.followRedirects,
|
||||||
|
}),
|
||||||
|
...(opts.maxRedirects !== undefined && {
|
||||||
|
maxRedirects: Math.min(opts.maxRedirects, 10),
|
||||||
|
}),
|
||||||
|
...(opts.timeout !== undefined && { timeout: opts.timeout }),
|
||||||
|
...(opts.decompress !== undefined && { decompress: opts.decompress }),
|
||||||
|
...(opts.cookies !== undefined && { cookies: opts.cookies }),
|
||||||
|
...(opts.keepAlive !== undefined && { keepAlive: opts.keepAlive }),
|
||||||
|
})),
|
||||||
|
O.filter((opts) => Object.keys(opts).length > 0)
|
||||||
|
)
|
||||||
|
|
||||||
export const convertDomainSetting = (
|
export const convertDomainSetting = (
|
||||||
input: InputDomainSetting
|
input: InputDomainSetting
|
||||||
): E.Either<Error, Pick<RelayRequest, "proxy" | "security">> => {
|
): E.Either<Error, Pick<RelayRequest, "proxy" | "security" | "meta">> => {
|
||||||
if (input.version !== "v1") {
|
if (input.version !== "v1") {
|
||||||
return E.left(new Error("Invalid version"))
|
return E.left(new Error("Invalid version"))
|
||||||
}
|
}
|
||||||
|
|
||||||
const security = convertSecurity(input.security)
|
const security = convertSecurity(input.security)
|
||||||
const proxy = convertProxy(input.proxy)
|
const proxy = convertProxy(input.proxy)
|
||||||
|
const options = convertOptions(input.options)
|
||||||
|
|
||||||
const result: Pick<RelayRequest, "proxy" | "security"> = {
|
const result: Pick<RelayRequest, "proxy" | "security" | "meta"> = {
|
||||||
proxy: O.isSome(proxy) ? proxy.value : undefined,
|
proxy: O.isSome(proxy) ? proxy.value : undefined,
|
||||||
security: O.isSome(security) ? security.value : undefined,
|
security: O.isSome(security) ? security.value : undefined,
|
||||||
|
meta: O.isSome(options) ? { options: options.value } : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
return E.right(result)
|
return E.right(result)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,9 @@ const defaultDomainConfig: InputDomainSetting = {
|
||||||
verifyPeer: true,
|
verifyPeer: true,
|
||||||
},
|
},
|
||||||
proxy: undefined,
|
proxy: undefined,
|
||||||
|
options: {
|
||||||
|
followRedirects: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KernelInterceptorAgentStore extends Service {
|
export class KernelInterceptorAgentStore extends Service {
|
||||||
|
|
@ -147,6 +150,15 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mergeOptions(
|
||||||
|
...settings: (Required<InputDomainSetting>["options"] | undefined)[]
|
||||||
|
): Required<InputDomainSetting>["options"] | undefined {
|
||||||
|
return settings.reduce(
|
||||||
|
(acc, setting) => (setting ? { ...acc, ...setting } : acc),
|
||||||
|
undefined as Required<InputDomainSetting>["options"] | undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private getMergedSettings(domain: string): InputDomainSetting {
|
private getMergedSettings(domain: string): InputDomainSetting {
|
||||||
const domainSettings = this.domainSettings.get(domain)
|
const domainSettings = this.domainSettings.get(domain)
|
||||||
const globalSettings =
|
const globalSettings =
|
||||||
|
|
@ -160,13 +172,17 @@ export class KernelInterceptorAgentStore extends Service {
|
||||||
domainSettings?.security
|
domainSettings?.security
|
||||||
),
|
),
|
||||||
proxy: this.mergeProxy(globalSettings?.proxy, domainSettings?.proxy),
|
proxy: this.mergeProxy(globalSettings?.proxy, domainSettings?.proxy),
|
||||||
|
options: this.mergeOptions(
|
||||||
|
globalSettings?.options,
|
||||||
|
domainSettings?.options
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return { version: "v1", ...result }
|
return { version: "v1", ...result }
|
||||||
}
|
}
|
||||||
|
|
||||||
public completeRequest(
|
public completeRequest(
|
||||||
request: Omit<PluginRequest, "proxy" | "security">
|
request: Omit<RelayRequest, "proxy" | "security" | "meta">
|
||||||
): PluginRequest {
|
): PluginRequest {
|
||||||
const host = new URL(request.url).host
|
const host = new URL(request.url).host
|
||||||
const settings = this.getMergedSettings(host)
|
const settings = this.getMergedSettings(host)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ const defaultDomainConfig: InputDomainSetting = {
|
||||||
verifyPeer: true,
|
verifyPeer: true,
|
||||||
},
|
},
|
||||||
proxy: undefined,
|
proxy: undefined,
|
||||||
|
options: {
|
||||||
|
followRedirects: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KernelInterceptorNativeStore extends Service {
|
export class KernelInterceptorNativeStore extends Service {
|
||||||
|
|
@ -117,6 +120,15 @@ export class KernelInterceptorNativeStore extends Service {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mergeOptions(
|
||||||
|
...settings: (Required<InputDomainSetting>["options"] | undefined)[]
|
||||||
|
): Required<InputDomainSetting>["options"] | undefined {
|
||||||
|
return settings.reduce(
|
||||||
|
(acc, setting) => (setting ? { ...acc, ...setting } : acc),
|
||||||
|
undefined as Required<InputDomainSetting>["options"] | undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private getMergedSettings(domain: string): InputDomainSetting {
|
private getMergedSettings(domain: string): InputDomainSetting {
|
||||||
const domainSettings = this.domainSettings.get(domain)
|
const domainSettings = this.domainSettings.get(domain)
|
||||||
const globalSettings =
|
const globalSettings =
|
||||||
|
|
@ -130,13 +142,17 @@ export class KernelInterceptorNativeStore extends Service {
|
||||||
domainSettings?.security
|
domainSettings?.security
|
||||||
),
|
),
|
||||||
proxy: this.mergeProxy(globalSettings?.proxy, domainSettings?.proxy),
|
proxy: this.mergeProxy(globalSettings?.proxy, domainSettings?.proxy),
|
||||||
|
options: this.mergeOptions(
|
||||||
|
globalSettings?.options,
|
||||||
|
domainSettings?.options
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return { version: "v1", ...result }
|
return { version: "v1", ...result }
|
||||||
}
|
}
|
||||||
|
|
||||||
public completeRequest(
|
public completeRequest(
|
||||||
request: Omit<RelayRequest, "proxy" | "security">
|
request: Omit<RelayRequest, "proxy" | "security" | "meta">
|
||||||
): RelayRequest {
|
): RelayRequest {
|
||||||
const host = new URL(request.url).host
|
const host = new URL(request.url).host
|
||||||
const settings = this.getMergedSettings(host)
|
const settings = this.getMergedSettings(host)
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,22 @@ pub struct CertificateConfig {
|
||||||
pub ca: Option<Vec<Bytes>>,
|
pub ca: Option<Vec<Bytes>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct RequestMeta {
|
||||||
|
pub options: Option<RequestOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RequestOptions {
|
||||||
|
pub timeout: Option<u64>,
|
||||||
|
pub follow_redirects: Option<bool>,
|
||||||
|
pub max_redirects: Option<u32>,
|
||||||
|
pub decompress: Option<bool>,
|
||||||
|
pub cookies: Option<bool>,
|
||||||
|
pub keep_alive: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
|
@ -327,6 +343,7 @@ pub struct Request {
|
||||||
pub auth: Option<AuthType>,
|
pub auth: Option<AuthType>,
|
||||||
pub security: Option<SecurityConfig>,
|
pub security: Option<SecurityConfig>,
|
||||||
pub proxy: Option<ProxyConfig>,
|
pub proxy: Option<ProxyConfig>,
|
||||||
|
pub meta: Option<RequestMeta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,88 @@ impl<'a> CurlRequest<'a> {
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let Some(ref meta) = self.request.meta else {
|
||||||
|
tracing::debug!("No meta configuration provided");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(ref options) = meta.options else {
|
||||||
|
tracing::debug!("No options in meta configuration");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(follow) = options.follow_redirects {
|
||||||
|
tracing::debug!(follow_redirects = follow, "Setting redirect behavior");
|
||||||
|
self.handle.follow_location(follow).map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to set follow_location");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to set redirect behavior".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(max) = options.max_redirects {
|
||||||
|
tracing::debug!(max_redirects = max, "Setting maximum redirects");
|
||||||
|
self.handle.max_redirections(max).map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to set max_redirections");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to set maximum redirects".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(timeout_ms) = options.timeout {
|
||||||
|
tracing::debug!(timeout_ms = timeout_ms, "Setting request timeout");
|
||||||
|
self.handle
|
||||||
|
.timeout(std::time::Duration::from_millis(timeout_ms))
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to set timeout");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to set timeout".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(decompress) = options.decompress {
|
||||||
|
if !decompress {
|
||||||
|
tracing::debug!("Disabling automatic decompression");
|
||||||
|
self.handle.accept_encoding("identity").map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to disable decompression");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to disable decompression".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(enable_cookies) = options.cookies {
|
||||||
|
tracing::debug!(enable_cookies = enable_cookies, "Setting cookie handling");
|
||||||
|
if enable_cookies {
|
||||||
|
self.handle.cookie_file("").map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to enable cookies");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to enable cookie handling".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(keep_alive) = options.keep_alive {
|
||||||
|
tracing::debug!(keep_alive = keep_alive, "Setting keep-alive");
|
||||||
|
self.handle.tcp_keepalive(keep_alive).map_err(|e| {
|
||||||
|
tracing::error!(error = %e, "Failed to set keep-alive");
|
||||||
|
RelayError::Network {
|
||||||
|
message: "Failed to set keep-alive".into(),
|
||||||
|
cause: Some(e.to_string()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
tracing::debug!("Basic request parameters set successfully");
|
tracing::debug!("Basic request parameters set successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2888,7 +2888,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "relay"
|
name = "relay"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/CuriousCorrelation/relay.git#7270b956321972269eea7cecdccbb2ae839bd0ff"
|
source = "git+https://github.com/CuriousCorrelation/relay.git#ed2329e4ebb71bb984c4705aa950cb9c3f9ff931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"curl",
|
"curl",
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,17 @@ export type CertificateType = {
|
||||||
data: Uint8Array;
|
data: Uint8Array;
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
export interface RequestOptions {
|
||||||
|
timeout?: number;
|
||||||
|
followRedirects?: boolean;
|
||||||
|
maxRedirects?: number;
|
||||||
|
decompress?: boolean;
|
||||||
|
cookies?: boolean;
|
||||||
|
keepAlive?: boolean;
|
||||||
|
}
|
||||||
|
export interface RequestMeta {
|
||||||
|
options?: RequestOptions;
|
||||||
|
}
|
||||||
export interface Request {
|
export interface Request {
|
||||||
id: number;
|
id: number;
|
||||||
url: string;
|
url: string;
|
||||||
|
|
@ -154,6 +165,7 @@ export interface Request {
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
meta?: RequestMeta;
|
||||||
}
|
}
|
||||||
export interface Response {
|
export interface Response {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -187,6 +187,19 @@ export type CertificateType =
|
||||||
password: string
|
password: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RequestOptions {
|
||||||
|
timeout?: number
|
||||||
|
followRedirects?: boolean
|
||||||
|
maxRedirects?: number
|
||||||
|
decompress?: boolean
|
||||||
|
cookies?: boolean
|
||||||
|
keepAlive?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestMeta {
|
||||||
|
options?: RequestOptions
|
||||||
|
}
|
||||||
|
|
||||||
export interface Request {
|
export interface Request {
|
||||||
id: number
|
id: number
|
||||||
url: string
|
url: string
|
||||||
|
|
@ -213,6 +226,8 @@ export interface Request {
|
||||||
password: string
|
password: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meta?: RequestMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Response {
|
export interface Response {
|
||||||
|
|
|
||||||
4
packages/hoppscotch-desktop/src-tauri/Cargo.lock
generated
4
packages/hoppscotch-desktop/src-tauri/Cargo.lock
generated
|
|
@ -4486,7 +4486,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "relay"
|
name = "relay"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/CuriousCorrelation/relay.git#7270b956321972269eea7cecdccbb2ae839bd0ff"
|
source = "git+https://github.com/CuriousCorrelation/relay.git#ed2329e4ebb71bb984c4705aa950cb9c3f9ff931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"curl",
|
"curl",
|
||||||
|
|
@ -5696,7 +5696,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-relay"
|
name = "tauri-plugin-relay"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/CuriousCorrelation/tauri-plugin-relay?rev=5d59b97fe331ca62e8be0454ff3f4e5f6185ae70#5d59b97fe331ca62e8be0454ff3f4e5f6185ae70"
|
source = "git+https://github.com/CuriousCorrelation/tauri-plugin-relay?rev=7cf09c1ad31e228758738c2f4e1c8fe9cc141291#7cf09c1ad31e228758738c2f4e1c8fe9cc141291"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"relay",
|
"relay",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ tauri-plugin-dialog = "2.2.0"
|
||||||
tauri-plugin-fs = "2.2.0"
|
tauri-plugin-fs = "2.2.0"
|
||||||
tauri-plugin-deep-link = "2.2.0"
|
tauri-plugin-deep-link = "2.2.0"
|
||||||
tauri-plugin-appload = { git = "https://github.com/CuriousCorrelation/tauri-plugin-appload", rev = "e05861959938b57479a1a81fa796735ebbd08c7c" }
|
tauri-plugin-appload = { git = "https://github.com/CuriousCorrelation/tauri-plugin-appload", rev = "e05861959938b57479a1a81fa796735ebbd08c7c" }
|
||||||
tauri-plugin-relay = { git = "https://github.com/CuriousCorrelation/tauri-plugin-relay", rev = "5d59b97fe331ca62e8be0454ff3f4e5f6185ae70" }
|
tauri-plugin-relay = { git = "https://github.com/CuriousCorrelation/tauri-plugin-relay", rev = "7cf09c1ad31e228758738c2f4e1c8fe9cc141291" }
|
||||||
axum = "0.8.1"
|
axum = "0.8.1"
|
||||||
tower-http = { version = "0.6.2", features = ["cors"] }
|
tower-http = { version = "0.6.2", features = ["cors"] }
|
||||||
random-port = "0.1.1"
|
random-port = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hoppscotch/plugin-relay": "github:CuriousCorrelation/tauri-plugin-relay#5d59b97fe331ca62e8be0454ff3f4e5f6185ae70",
|
"@hoppscotch/plugin-relay": "github:CuriousCorrelation/tauri-plugin-relay#7cf09c1ad31e228758738c2f4e1c8fe9cc141291",
|
||||||
"@tauri-apps/plugin-dialog": "2.0.1",
|
"@tauri-apps/plugin-dialog": "2.0.1",
|
||||||
"@tauri-apps/plugin-fs": "2.0.2",
|
"@tauri-apps/plugin-fs": "2.0.2",
|
||||||
"@tauri-apps/plugin-shell": "2.2.1",
|
"@tauri-apps/plugin-shell": "2.2.1",
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ export const implementation: VersionedAPI<RelayV1> = {
|
||||||
auth: request.auth,
|
auth: request.auth,
|
||||||
security: request.security,
|
security: request.security,
|
||||||
proxy: request.proxy,
|
proxy: request.proxy,
|
||||||
|
meta: request.meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
return execute(pluginRequest)
|
return execute(pluginRequest)
|
||||||
|
|
|
||||||
|
|
@ -1223,8 +1223,8 @@ importers:
|
||||||
packages/hoppscotch-kernel:
|
packages/hoppscotch-kernel:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hoppscotch/plugin-relay':
|
'@hoppscotch/plugin-relay':
|
||||||
specifier: github:CuriousCorrelation/tauri-plugin-relay#5d59b97fe331ca62e8be0454ff3f4e5f6185ae70
|
specifier: github:CuriousCorrelation/tauri-plugin-relay#7cf09c1ad31e228758738c2f4e1c8fe9cc141291
|
||||||
version: '@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/5d59b97fe331ca62e8be0454ff3f4e5f6185ae70'
|
version: '@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/7cf09c1ad31e228758738c2f4e1c8fe9cc141291'
|
||||||
'@tauri-apps/api':
|
'@tauri-apps/api':
|
||||||
specifier: 2.1.1
|
specifier: 2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
|
@ -1859,8 +1859,8 @@ packages:
|
||||||
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e05861959938b57479a1a81fa796735ebbd08c7c}
|
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-appload/tar.gz/e05861959938b57479a1a81fa796735ebbd08c7c}
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
|
||||||
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/5d59b97fe331ca62e8be0454ff3f4e5f6185ae70':
|
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/7cf09c1ad31e228758738c2f4e1c8fe9cc141291':
|
||||||
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/5d59b97fe331ca62e8be0454ff3f4e5f6185ae70}
|
resolution: {tarball: https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/7cf09c1ad31e228758738c2f4e1c8fe9cc141291}
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0':
|
'@alloc/quick-lru@5.2.0':
|
||||||
|
|
@ -16014,7 +16014,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.1.1
|
'@tauri-apps/api': 2.1.1
|
||||||
|
|
||||||
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/5d59b97fe331ca62e8be0454ff3f4e5f6185ae70':
|
'@CuriousCorrelation/plugin-relay@https://codeload.github.com/CuriousCorrelation/tauri-plugin-relay/tar.gz/7cf09c1ad31e228758738c2f4e1c8fe9cc141291':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.1.1
|
'@tauri-apps/api': 2.1.1
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue