>>FIRST MISSION//LOGS//cloudflare//creating-a-cloudflare-worker-using-rust-for-fetching-resources.MD
[ + ] TARGET_LOCKED200 OK
DEC 15, 2020timer 3 MIN READSYS_TAG: cloudflare STATUS: VERIFIED_WORKING

Creating A Cloudflare Worker Using Rust for Fetching Resources

ENGINE: KERNEL // STABLE
BASE_ARCH: x86_64
HOST: VASTORIGINS.COM
GPG: OKSIG_ID: 0x4EB27DB0
Creating A Cloudflare Worker Using Rust for Fetching Resources
SPEC_IMAGE // PROVENANCE_VERIFIED

To learn something new, you need to try new things and not be afraid to be wrong.

~ Roy T. Bennett.

Have you ever worked on Web Assembly? Now is a good time to start, first because it’s already supported by your browser, from Chromium based ones (e.g., Edge, Brave, Google Chrome) to Firefox, and it has been enabled by default since 2019. Plus, you can use your favorite language (technically not all of them, but more every year) to develop web assembly. The barrier to entry has never been lower.

In this quick tutorial we will be using Rust, not the game though.

Ferris, the Rust language mascot.

Ferris saying hello 👋.
Come on, let’s jump in! 💪

✅ Prerequisites

First of all, you must have a Cloudflare account. Second, the Rust tool chain installed in your computer, and I also assume you are currently running Windows 10 or a Linux distro with a proper environment set.

If you don’t have Rust, go to this link in order to install it. And for the Cloudflare account, basically just use the free tier, which gives you 100,000 worker calls per day and free Key-Value (KV) storage. That is plenty for a personal project, and honestly more than enough to learn the ropes.

🚀 So where do we start?

The first thing we need to do is install wrangler, which is a command-line tool specifically developed by Cloudflare to complement the development and deployment of Workers. Install the wrangler tool using the cargo command utility:

cargo install wrangler

The command above will first fetch the source from crates.io and compile it as a binary. The command will also automatically install it on your ~/.cargo/bin directory. Go grab a coffee while it compiles, this one takes a bit.

💡: Cloudflare Worker is similar to AWS Lambda and Azure Cloud Function. They’re both under the serverless computing category. The appeal of Workers in particular is that your code runs at the edge, close to your users, on Cloudflare’s network of data centers, instead of one region far away.

After the installation of wrangler, you need to authenticate using your Cloudflare account API key, which you can get on the user settings panel:

wrangler login

If all works well, the next thing we need to do is generate the cargo project using the wrangler command line. Execute the code below to generate a cargo project using the Rust WASM worker template:

wrangler generate worker_fetch_demo https://github.com/cloudflare/rustwasm-worker-template.git --type="rust"

After that, go inside the folder named worker_fetch_demo to edit the file cargo.toml. Add the following crate dependencies:

cfg-if = "0.1.2"
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
console_error_panic_hook = { version = "0.1.1", optional = true }
wee_alloc = { version = "0.4.2", optional = true }
futures = { version = "0.3", default-features = false }
js-sys = "0.3.45"
wasm-bindgen-futures = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_derive = "^1.0.59"
serde_json = "1.0"
log = "0.4"
console_log = { version = "0.2", optional = true }

The wasm-bindgen package is the most important, as that is what links the package to call into JavaScript scopes and other web and web assembly related functionalities. It is essentially the bridge between Rust and the browser world. You also need to add the web-sys package, as that will provide basic mappings and calls to JavaScript functions.

You’ll be able to get to know what the other packages are for if you’ve already read the Rust Programming Language Book, but here is a quick map: js-sys gives you bindings to the JavaScript standard library, serde handles serialization, and wasm-bindgen-futures is what lets us work with promises from Rust.

[dependencies.web-sys]
version = "0.3.45"
features = [
  'Headers',
  'Request',
  'RequestInit',
  'Response',
  'ServiceWorkerGlobalScope',
]

After adding those crate dependencies, it will automatically be fetched on build or upon a call to cargo update.

Next thing we modify is the file worker > worker.js. This file serves as the main entry-point of our program that will call our compiled wasm files. We need to add minor modifications to it, specifically capturing requests and serving the wasm response as JSON:

async function handleRequest(request) {
  const { test } = wasm_bindgen;
  await wasm_bindgen(wasm);

  const data = await test();
  return new Response(JSON.stringify(data), {
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
    },
    status: 200,
  });
}

We move on now to the rust files. 🦀

On the file src > lib.rs add the following code. This particular instruction will add a basic initialization for our console log (similar to JavaScript console.log) if the console_log dependency is present:

cfg_if! {
    if #[cfg(feature = "console_log")] {
        fn init_log() {
            console_log::init_with_level(Level::Trace).expect("error initializing log");
        }
    } else {
        fn init_log() {}
    }
}

Next, we add a function that will hook to js_sys to return the ServiceWorkerGlobalScope.

Specifically, on Cloudflare, the normal browser fetch call won’t work, as the workers run on a headless V8 JavaScript engine. That’s why we need to hook into the internal HTTP clients for service workers:

pub fn worker_global_scope() -> Option<web_sys::ServiceWorkerGlobalScope> {
    js_sys::global().dyn_into::<web_sys::ServiceWorkerGlobalScope>().ok()
}

After adding our worker_global_scope, we proceed with editing the greet function. First, rename it to run, then add our first instruction to hook rust panics to console_error. Then call init_log to initialize basic logging functionality:

std::panic::set_hook(Box::new(console_error_panic_hook::hook));
init_log();

Then we initialize our request with the method GET. You could also use other HTTP methods (e.g., POST, PUT, DELETE, …). The method depends on your application needs and the endpoints you want to call:

let mut opts = RequestInit::new();
opts.method("GET");

Next will be creating the request payload that we will submit to our custom fetch. The instruction will contain the endpoint and request options:

let request = Request::new_with_str_and_init(
    "https://httpbin.org/get",
    &opts
)?;

After finishing that, we will now scope and call the function we created earlier. Then we wrap it in a future (asynchronous method calls, similar to JavaScript promises if you’re more familiar with that term):

let global = worker_global_scope().unwrap();
let resp_value = JsFuture::from(global.fetch_with_request(&request)).await?;

assert!(resp_value.is_instance_of::<Response>());
let resp: Response = resp_value.dyn_into().unwrap();
let json = JsFuture::from(resp.json()?).await?;

On the returned response, unwrap it and return its JSON value.

Here is our full wasm function that will be called on our worker.js that we defined earlier above:

#[wasm_bindgen]
pub async fn test() -> Result<JsValue, JsValue> {
    std::panic::set_hook(Box::new(console_error_panic_hook::hook));
    init_log();

    let mut opts = RequestInit::new();
    opts.method("GET");

    let request = Request::new_with_str_and_init(
        "https://httpbin.org/get",
        &opts
    )?;

    let global = worker_global_scope().unwrap();
    let resp_value = JsFuture::from(global.fetch_with_request(&request)).await?;

    assert!(resp_value.is_instance_of::<Response>());
    let resp: Response = resp_value.dyn_into().unwrap();
    let json = JsFuture::from(resp.json()?).await?;

    Ok(json)
}

Now, we need to test it, to see if everything’s okay. Spin up a local server using the following command below:

wrangler dev

Test everything and try to call the URL returned by wrangler dev using Postman or Insomnia HTTP client. If everything is working fine, it’s now time to deploy the worker to a live server:

wrangler publish

After running the command above, it will return a live worker URL which you can now access from anywhere in the world.

That’s all guys!

🧠 Final Thoughts

You can find the complete repository here.

This is not the only way to call fetch on Cloudflare worker rust, the other method involves hooking directly to the JavaScript exposed fetch (kindly look at Cloudflare example files). Both approaches work; this one just happens to be the one that clicked for me. What I love about this setup is how little it takes: Rust compiled to wasm, one small entry point, and your code running at the edge of the internet. If you are new to wasm, this is a perfect first project, and if you have any questions kindly leave a comment or DM me 😉.

Follow me for similar article, tips, and tricks ❤.

[ SIGNATURE VERIFIED // GPG 0x4EB27DB0 // PROVENANCE SECURE ]
➔ RETURN TO LOGS