api-client/packages/hoppscotch-desktop/plugin-workspace/relay
Shreyas 72ff950d91
fix(relay): avoid override with header passthrough (#4931)
The current implementation causes duplicate `Content-Type` headers when users
override headers in the UI or use OAuth2 authentication with the agent.
Web servers receive multiple `Content-Type` headers which causes
undefined behavior and 400 errors for backends that don't accept duplicate headers.

This also fixes inconsistent behavior when overriding the `Content-Type` header
with custom values (e.g., `application/json;v=2`).

While HTTP/1.1 headers are case-insensitive per RFC 7230, inconsistent handling
across server implementations can treat differently-cased variations (e.g.,
"Content-Type" vs "content-type") as distinct headers. HTTP/2 (RFC 7540) mandates
converting all header field names to lowercase, which would prevent this issue.

This patch removes the automatic content-type header insertion, allowing user-defined
headers to take precedence without duplication. The is a temporary
workaround until we implement a HTTP/2-compliant solution with proper normalization.

This was implemented initially to support moving lower level handling
towards the kernel, although since the larger refactor has been slightly
deferred in favor of stability, this change is suitable for current
state.

This will be revisited when we implement HTTP/2 compliant header handling in the
kernel layer as part of our upcoming kernel efforts.

Use the following request to test this out on Desktop app and Agent and
override `Content-Type` header to `application/json;=v2`:

```
curl --request POST \
  --url 'https://echo.qubit.codes/?qp=1' \
  --header 'Content-Type: application/json;v=2' \
  --data '{ "test-key": "test-value" }'
```
2025-03-25 16:34:27 +05:30
..
src fix(relay): avoid override with header passthrough (#4931) 2025-03-25 16:34:27 +05:30
.envrc feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
.gitignore feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
Cargo.lock fix(desktop): preserve formdata ordering (#4892) 2025-03-19 14:04:13 +05:30
Cargo.toml fix(desktop): preserve formdata ordering (#4892) 2025-03-19 14:04:13 +05:30
devenv.lock feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
devenv.nix feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
devenv.yaml feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
LICENSE.md feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30
README.md feat: platform independent core and the new desktop app (#4684) 2025-02-28 00:01:25 +05:30

Relay

A HTTP request-response relay used by Hoppscotch Desktop and Hoppscotch Agent for more advanced request handling including custom headers, certificates, proxies, and local system integration.

Important

This crate is only available via GitHub and not published on crates.io right now.

GitHub License MIT Rust

Installation

Add to your Cargo.toml:

[dependencies]
relay = { git = "https://github.com/CuriousCorrelation/relay.git" }

Features

  • 🦀 Blazingly fast!
  • HTTP client built on libcurl
  • HTTP/1.1, HTTP/2.0, HTTP/3.0 support
  • Security with SSL/TLS certificate management
  • Proxy support with authentication
  • Multiple authentication methods (Basic, Bearer, Digest)
  • Content handling (JSON, Form Data, Binary)
  • Custom security configurations
  • Async request execution with cancellation support

Usage

use relay::{Request, Response, execute};

let request = Request {
    id: 1,
    url: "https://api.example.com".to_string(),
    method: Method::Get,
    version: Version::Http2,
    // ... configure other options
};

let response = execute(request).await?;

Note

All requests are executed asynchronously and can be cancelled using the cancel(request_id) function.

Security Features

Tip

You can configure certificate validation, host verification, and custom certificates:

let security_config = SecurityConfig {
    validate_certificates: Some(true),
    verify_host: Some(true),
    certificates: Some(CertificateConfig {
        client: Some(CertificateType::Pem { 
            cert: cert_data,
            key: key_data 
        }),
        ca: Some(vec![ca_cert_data])
    })
};

Error Handling

The crate uses a custom error type RelayError that provides information about failures:

#[derive(Error)]
pub enum RelayError {
    Network { message: String, cause: Option<String> },
    Certificate { message: String, cause: Option<String> },
    Parse { message: String, cause: Option<String> },
    // ... other variants
}

Requirements

  • Rust 1.77.2 or later
  • OpenSSL development libraries
  • libcurl with SSL and HTTP/2.0 support

Warning

This crate uses custom forks of some dependencies for NTLM support and consistent OpenSSL backend across platforms.

License

Code: (c) 2024 - CuriousCorrelation

MIT or MIT/Apache 2.0 where applicable.