From 3419f5fc420302008e96191df18f3b7032342e66 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 1 Jul 2024 16:58:00 -0400 Subject: [PATCH] zed_extension_api: Add `fetch` (#13716) This PR adds a new `fetch` function to the `zed_extension_api` to allow fetching a URL through the Wasm host. Currently we only support GET requests and return the response body as a string. Release Notes: - N/A --- .../src/wasm_host/wit/since_v0_0_7.rs | 39 ++++++++++++++++++- crates/extension_api/src/extension_api.rs | 1 + .../wit/since_v0.0.7/extension.wit | 1 + .../wit/since_v0.0.7/http-client.wit | 16 ++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 crates/extension_api/wit/since_v0.0.7/http-client.wit diff --git a/crates/extension/src/wasm_host/wit/since_v0_0_7.rs b/crates/extension/src/wasm_host/wit/since_v0_0_7.rs index 08a2993f57..5806c6ce86 100644 --- a/crates/extension/src/wasm_host/wit/since_v0_0_7.rs +++ b/crates/extension/src/wasm_host/wit/since_v0_0_7.rs @@ -1,10 +1,12 @@ use crate::wasm_host::{wit::ToWasmtimeResult, WasmState}; use ::settings::Settings; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use async_trait::async_trait; +use futures::AsyncReadExt; use futures::{io::BufReader, FutureExt as _}; +use http::AsyncBody; use language::{ language_settings::AllLanguageSettings, LanguageServerBinaryStatus, LspAdapterDelegate, }; @@ -101,6 +103,41 @@ impl HostWorktree for WasmState { #[async_trait] impl common::Host for WasmState {} +#[async_trait] +impl http_client::Host for WasmState { + async fn fetch( + &mut self, + req: http_client::HttpRequest, + ) -> wasmtime::Result> { + maybe!(async { + let url = &req.url; + + let mut response = self + .host + .http_client + .get(url, AsyncBody::default(), true) + .await?; + + if response.status().is_client_error() || response.status().is_server_error() { + bail!("failed to fetch '{url}': status code {}", response.status()) + } + + let mut body = Vec::new(); + response + .body_mut() + .read_to_end(&mut body) + .await + .with_context(|| format!("failed to read response body from '{url}'"))?; + + Ok(http_client::HttpResponse { + body: String::from_utf8(body)?, + }) + }) + .await + .to_wasmtime_result() + } +} + #[async_trait] impl nodejs::Host for WasmState { async fn node_binary_path(&mut self) -> wasmtime::Result> { diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index 5e9f3ad12f..af0ad0a41b 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -19,6 +19,7 @@ pub use wit::{ github_release_by_tag_name, latest_github_release, GithubRelease, GithubReleaseAsset, GithubReleaseOptions, }, + zed::extension::http_client::{fetch, HttpRequest, HttpResponse}, zed::extension::nodejs::{ node_binary_path, npm_install_package, npm_package_installed_version, npm_package_latest_version, diff --git a/crates/extension_api/wit/since_v0.0.7/extension.wit b/crates/extension_api/wit/since_v0.0.7/extension.wit index 522df4ccb3..9e4568c2bf 100644 --- a/crates/extension_api/wit/since_v0.0.7/extension.wit +++ b/crates/extension_api/wit/since_v0.0.7/extension.wit @@ -2,6 +2,7 @@ package zed:extension; world extension { import github; + import http-client; import platform; import nodejs; diff --git a/crates/extension_api/wit/since_v0.0.7/http-client.wit b/crates/extension_api/wit/since_v0.0.7/http-client.wit new file mode 100644 index 0000000000..e1f7b69d49 --- /dev/null +++ b/crates/extension_api/wit/since_v0.0.7/http-client.wit @@ -0,0 +1,16 @@ +interface http-client { + /// An HTTP request. + record http-request { + /// The URL to which the request should be made. + url: string, + } + + /// An HTTP response. + record http-response { + /// The response body. + body: string, + } + + /// Performs an HTTP request and returns the response. + fetch: func(req: http-request) -> result; +}