Files
SpacetimeDB/crates/bindings/Cargo.toml
Phoebe Goldman 7df8719b61 Add procedure HTTP request API for WASM modules and the Rust module bindings library (#3684)
# Description of Changes

Closes #3517 .

With this PR, procedures (at least, those defined in Rust modules) can
perform HTTP requests! This is performed through a new field on the
`ProcedureContext`, `http: HttpClient`, which has a method `send` for
sending an `http::Request`, as well as a convenience wrapper `get`.

Internally, these methods hit the `procedure_http_request` ABI call /
host function, which uses reqwest to perform an HTTP request. The
request is run with a user-configurable timeout which defaults and is
clamped to 500 ms.
Rather than exposing the HTTP stream to modules, we download the entire
response body immediately, within the same timeout.

I've added an example usage of `get` to `module-test` which performs a
request against `localhost:3000` to read its own schema/moduledef.

This PR also makes all procedure-related definitions in the Rust module
bindings library `#[cfg(feature = "unstable")]`, as per #3644 . The
rename of the `/v1/database/:name/procedure/:name` route is not included
in this PR, so this does not close #3644 .

Left as TODOs are:
- Metrics for recording request and response size.
- Improving performance by stashing a long-lived `reqwest::Client`
someplace.
  Currently we build a new `Client` for each request.
- Improving performance (possibly) by passing the request-future to the
global tokio executor
  rather than running it on the single-threaded database executor.

# API and ABI breaking changes

Adds new APIs, which are marked as unstable. Adds a new ABI, which is
not unstable in any meaningful way (we can't really do that). Marks
unreleased APIs as unstable. Does not affect any pre-existing
already-released APIs or ABIs.

# Expected complexity level and risk

3 or so: networking is scary, and even though we impose a timeout which
prevents these connections from being truly long-lived, they're still
potentially long-lived on the scale of Tokio futures. It's possible that
running them on the database core is problematic in some way, and so
what I've left as a performance TODO could actually be a
concurrency-correctness issue.

# Testing

- [x] Manually wrote and executed some procedures which make HTTP
requests.
- [x] Added two automated tests to the `sdk-test` suite,
`procedure::http_ok` and `procedure::http_err`, which make successful
and failing requests respectively, then return its result. A client then
makes some assertions about the result.

---------

Co-authored-by: Noa <coolreader18@gmail.com>
2025-11-20 20:47:35 +00:00

48 lines
1.4 KiB
TOML

[package]
name = "spacetimedb"
version.workspace = true
edition.workspace = true
license-file = "LICENSE"
description = "Easy support for interacting between SpacetimeDB and Rust."
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "spacetimedb" # The name of the target.
path = "src/lib.rs" # The source file of the target.
# Benching off, because of https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options
bench = false
[features]
default = ["rand"]
rand = ["rand08"]
rand08 = ["dep:rand08", "dep:getrandom02"]
unstable = ["spacetimedb-bindings-sys/unstable"]
[dependencies]
spacetimedb-bindings-sys.workspace = true
spacetimedb-lib.workspace = true
spacetimedb-bindings-macro.workspace = true
spacetimedb-primitives.workspace = true
bytemuck.workspace = true
bytes.workspace = true
derive_more.workspace = true
http.workspace = true
log.workspace = true
scoped-tls.workspace = true
rand08 = { workspace = true, optional = true }
# we depend on getrandom and enable the `custom` feature, so that
# if someone tries to use rand's ThreadRng, it will fail to link
# because no one defined __getrandom_custom
getrandom02 = { workspace = true, optional = true, features = ["custom"] }
serde_json.workspace = true
[dev-dependencies]
insta.workspace = true
trybuild.workspace = true
[lints]
workspace = true