Skip to content

Commit

Permalink
Merge pull request #243 from cloudflare/zeb/service-bindings
Browse files Browse the repository at this point in the history
Add service binding support
  • Loading branch information
zebp authored Nov 17, 2022
2 parents f3e65bd + c999534 commit b8f4648
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 13 deletions.
2 changes: 1 addition & 1 deletion worker-build/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn ensure_wasm_pack() -> Result<()> {
if is_installed("wasm-pack")?.is_none() {
println!("Installing wasm-pack...");
let exit_status = Command::new("cargo")
.args(&["install", "wasm-pack"])
.args(["install", "wasm-pack"])
.spawn()?
.wait()?;

Expand Down
8 changes: 4 additions & 4 deletions worker-build/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ where
let exit_status = Command::new("wasm-pack")
.arg("build")
.arg("--no-typescript")
.args(&["--target", "bundler"])
.args(&["--out-dir", OUT_DIR])
.args(&["--out-name", OUT_NAME])
.args(["--target", "bundler"])
.args(["--out-dir", OUT_DIR])
.args(["--out-name", OUT_NAME])
.args(args)
.spawn()?
.wait()?;
Expand Down Expand Up @@ -122,7 +122,7 @@ fn bundle(esbuild_path: &Path) -> Result<()> {
let path = PathBuf::from(OUT_DIR).join(WORKER_SUBDIR).canonicalize()?;
let esbuild_path = esbuild_path.canonicalize()?;
let mut command = Command::new(esbuild_path);
command.args(&[
command.args([
"--external:./index.wasm",
"--format=esm",
"--bundle",
Expand Down
3 changes: 3 additions & 0 deletions worker-sandbox/remote-service/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"main": "./service.js"
}
3 changes: 3 additions & 0 deletions worker-sandbox/remote-service/service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
addEventListener('fetch', event => {
event.respondWith(new Response('hello world'))
})
2 changes: 2 additions & 0 deletions worker-sandbox/remote-service/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name = "remote-service"
type = "javascript"
11 changes: 11 additions & 0 deletions worker-sandbox/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,17 @@ pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Respo
Ok(resp)
}
})
.get_async("/remote-by-request", |req, ctx| async move {
let fetcher = ctx.service("remote")?;
fetcher.fetch_request(req).await
})
.get_async("/remote-by-path", |req, ctx| async move {
let fetcher = ctx.service("remote")?;
let mut init = RequestInit::new();
init.with_method(Method::Post);

fetcher.fetch(req.url()?.to_string(), Some(init)).await
})
.or_else_any_method_async("/*catchall", |_, ctx| async move {
console_log!(
"[or_else_any_method_async] caught: {}",
Expand Down
9 changes: 9 additions & 0 deletions worker-sandbox/tests/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,12 @@ fn cache_api() {
let body: serde_json::Value = post(delete_endpoint.as_str(), |r| r).json().unwrap();
assert_eq!("ResponseNotFound", body);
}

#[test]
fn test_service_binding() {
let body: String = get("remote-by-request", |r| r).text().unwrap();
assert_eq!(body, "hello world");

let body: String = get("remote-by-path", |r| r).text().unwrap();
assert_eq!(body, "hello world");
}
7 changes: 7 additions & 0 deletions worker-sandbox/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ kv_namespaces = [

vars = { SOME_VARIABLE = "some value" }

[[services]]
binding = "remote"
service = "remote-service"

[miniflare.mounts]
remote-service = "./remote-service"

[durable_objects]
bindings = [{ name = "COUNTER", class_name = "Counter" }, { name = "ALARM", class_name = "AlarmObject" }]

Expand Down
9 changes: 7 additions & 2 deletions worker/src/env.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::error::Error;
use crate::Result;
use crate::{durable::ObjectNamespace, DynamicDispatcher};
use crate::{durable::ObjectNamespace, DynamicDispatcher, Fetcher, Result};

use js_sys::Object;
use wasm_bindgen::{prelude::*, JsCast, JsValue};
Expand Down Expand Up @@ -51,6 +50,12 @@ impl Env {
pub fn dynamic_dispatcher(&self, binding: &str) -> Result<DynamicDispatcher> {
self.get_binding(binding)
}

/// Get a [Service Binding](https://developers.cloudflare.com/workers/runtime-apis/service-bindings/)
/// for Worker-to-Worker communication.
pub fn service(&self, binding: &str) -> Result<Fetcher> {
self.get_binding(binding)
}
}

pub trait EnvBinding: Sized + JsCast {
Expand Down
40 changes: 35 additions & 5 deletions worker/src/fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use wasm_bindgen::JsCast;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use worker_sys::{Fetcher as FetcherSys, Response as ResponseSys};

use crate::{Request, RequestInit, Response, Result};
use crate::{env::EnvBinding, Request, RequestInit, Response, Result};

/// A struct for invoking fetch events to other Workers.
pub struct Fetcher(FetcherSys);

impl Fetcher {
/// Invoke a fetch event in a worker with a path and optionally a [RequestInit].
/// Invoke a fetch event in a worker with a url and optionally a [RequestInit].
pub async fn fetch(
&self,
path: impl Into<String>,
url: impl Into<String>,
init: Option<RequestInit>,
) -> Result<Response> {
let path = path.into();
let path = url.into();
let promise = match init {
Some(ref init) => self.0.fetch_with_str_and_init(&path, &init.into()),
None => self.0.fetch_with_str(&path),
Expand All @@ -32,6 +32,36 @@ impl Fetcher {
}
}

impl EnvBinding for Fetcher {
const TYPE_NAME: &'static str = "Fetcher";
}

impl JsCast for Fetcher {
fn instanceof(val: &wasm_bindgen::JsValue) -> bool {
val.is_instance_of::<Fetcher>()
}

fn unchecked_from_js(val: wasm_bindgen::JsValue) -> Self {
Self(val.into())
}

fn unchecked_from_js_ref(val: &wasm_bindgen::JsValue) -> &Self {
unsafe { &*(val as *const JsValue as *const Self) }
}
}

impl From<Fetcher> for JsValue {
fn from(service: Fetcher) -> Self {
JsValue::from(service.0)
}
}

impl AsRef<wasm_bindgen::JsValue> for Fetcher {
fn as_ref(&self) -> &wasm_bindgen::JsValue {
&self.0
}
}

impl From<FetcherSys> for Fetcher {
fn from(inner: FetcherSys) -> Self {
Self(inner)
Expand Down
8 changes: 7 additions & 1 deletion worker/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
http::Method,
request::Request,
response::Response,
Result,
Fetcher, Result,
};

type HandlerFn<D> = fn(Request, RouteContext<D>) -> Result<Response>;
Expand Down Expand Up @@ -94,6 +94,12 @@ impl<D> RouteContext<D> {
pub fn param(&self, key: &str) -> Option<&String> {
self.params.get(key)
}

/// Get a [Service Binding](https://developers.cloudflare.com/workers/runtime-apis/service-bindings/)
/// for Worker-to-Worker communication.
pub fn service(&self, binding: &str) -> Result<Fetcher> {
self.env.service(binding)
}
}

impl<'a> Router<'a, ()> {
Expand Down

0 comments on commit b8f4648

Please sign in to comment.