Internet of Functions: webhooks

Sep 28, 2020 • 4 minutes to read

A FaaS function is designed to perform a unit of specialized work. It is part of a larger Internet-based system. The vision of serverless computing is build fully fledged web applications with a network of functions and storage services. We need to chain FaaS functions, web gateways, and messaging queues together to create pipelines for data processing.

The Second State FaaS supports using webhooks as function input and output. The FaaS function can take web-based content as input arguments, and send the return value to another web service or function. Let’s check out some examples.

Pre-fetch input arguments from URLs

There are multiple ways to connect a URL or web resource to a function call’s input argument.

The first approach is to specify an URL in the HTTP request header named SSVM_Fetch. The function must take a single &[u8] argument. The FaaS first retrieves data from the URL, and then passes the data as an input argument for the function. See an example here.

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/run/118/watermark/bytes' \
--header 'SSVM_Fetch: https://www.secondstate.io/demo/dog.png' --output tmp.png

The second approach is to specify an URL in a fetch_input_# named part in a multipart request. In the following example, data content at https://www.secondstate.io/demo/dog.png is passed to the function call as the 2nd argument.

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/97/watermark/bytes' \
--header 'Content-Type: application/json' \
--form 'input_1=Howdy Second State' \
--form 'fetch_input_2=https://www.secondstate.io/demo/dog.png' \
--output tmp.png

The fetch_input_# parts take not only GET URLs but also full Node.js HTTP options JSON objects. That allows the function caller to specify how to access restricted web resources, as he can pass in access credentials in the HTTP request JSON object. Here is an example.

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/97/watermark/bytes' \
--header 'Content-Type: application/json' \
--form 'input_1=Howdy Second State' \
--form 'fetch_input_2={ "hostname": "www.secondstate.io",
    "path": "/demo/dog.png",
    "method": "GET",
    "headers": {
        "Content-Type": "application/json",
        "authorization": "Bearer ABCD123456789"
    }
}' \
--output tmp.png

At this point, each input URL maps to a byte array (&[u8]) input argument on the Rust function. If the content at the input URL is a text string, you will need to convert the &[u8] to &str or String inside your Rust function code.

Webhook for return value

A Second State FaaS function can not only capture input arguments from upstream URLs, but also send the return value to a downstream URL for further processing. Let’s look at an example. The hello-callback function takes a string argument, and then sends an email through the sendgrid service.

The call argument is the email subject and body. The return value of the function is a JSON object. The format of this JSON object conforms to sendgrid’s API specification. FaaS forwards this JSON return value to sendgrid, which sends the email.

use wasm_bindgen::prelude::*;
use serde_json::json;

#[wasm_bindgen]
pub fn say(s: &str) -> String {
  let ret = json!(
    {
        "personalizations": [{
            "to": [{
                "email": "michael@michaelyuan.com"
            }]
        }],
        "from": {
            "email": "michael@secondstate.io"
        },
        "subject": &s,
        "content": [{
            "type": "text/plain",
            "value": &("This is a message from Second State: ".to_owned() + s)
        }]
    });
  return ret.to_string();
}

Build and deploy the WebAssembly byte code. Notice that the response from the FaaS contains not only a wasm_id but also a SSVM_Admin_Key. The SSVM_Admin_Key is required for the user to modify the configuration of the function, which we will see next.

$ ssvmup build
... ...
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/executables' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM-Description: say hello' \
--data-binary '@pkg/hello_lib_bg.wasm'
{"wasm_id":123,"wasm_sha256":"0xec9e4c7d01920f...644bed9bf7922","SSVM_Usage_Key":"00000000-0000-0000-0000-000000000000","SSVM_Admin_Key":"b425089...8bfa58e6"}

Using the SSVM_Admin_Key, we can create a callback for the function. The callback object is in the format of Node.js HTTP options JSON. Notice the authorization Bearer token that authorizes the function for sendgrid operations. You can get an authorization token from sendgrid for free. Just make sure that it is linked to the from email address in your code.

$ curl --location --request PUT 'https://rpc.ssvm.secondstate.io:8081/api/callback/123' \
--header 'Content-Type: application/json' \
--header 'SSVM_Admin_Key: bd1d0a86-033c-4abb-a0ae-152a09266696' \
--data-raw '{"hostname": "api.sendgrid.com","path": "/v3/mail/send","method": "POST","port": 443,"headers":{"Content-Type": "application/json","authorization": "Bearer SG.xxxx"}}'

The request below shows how to send an email from this function.

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/run/123/say' \
--header 'Content-Type: text/plain' \
--data-raw 'Hello Second State FaaS'
... Sends email ...

What’s next

The Second State FaaS provides a rich set of configuration options to link the input and output of functions to web resources. However, in some use case, we need to programmatically access web resources directly from the function. Here is where the http_proxy command comes into play. We will explore http_proxy in the next article.

RustJavaScriptWebAssemblyNode.jsFaaSRust FaaSServerlesscloud computing
A high-performance, extensible, and hardware optimized WebAssembly Virtual Machine for automotive, cloud, AI, and blockchain applications