In the previous two articles, we have learnt how to use text, JSON, binary data, and even webhook resources as FaaS function call arguments. But we have also seen the need to mix multiple arguments in a single function call. In this article, we will demonstrate how to do that.
The source code for the custom watermark example in this article is available on Github.
Watermark an image
The Rust function takes two call arguments: a string for the watermark text, and a byte array for an image (in a &[u8]
). It adds a watermark text to the image, and then returns the watermarked image in a byte array (in a Vec<u8>
).
#[wasm_bindgen]
pub fn watermark (watermark_text: &str, img_buf: &[u8]) -> Vec<u8> {
// Read the input image
let mut img = image::load_from_memory(img_buf).unwrap();
let (w,h) = img.dimensions();
let scale = Scale {
x: w as f32 /10.0,
y: h as f32 /10.0,
};
// Prepare fonts for the watermark
let font = Vec::from(include_bytes!("DejaVuSans.ttf") as &[u8]);
let font = Font::try_from_vec(font).unwrap();
// Draw the watermark on the input image
drawing::draw_text_mut(&mut img, image::Rgba([255u8, 255u8, 255u8, 255u8]), 0+(h/10),h/2, scale, &font, watermark_text);
// Write and return the watermarked image
let mut buf = vec![];
img.write_to(&mut buf, image::ImageOutputFormat::Png).unwrap();
return buf;
}
Before compiling, make sure that your Cargo.toml
file has declared the correct dependencies.
[dependencies]
image = "=0.23.7"
imageproc = "=0.21.0"
rusttype = "=0.9.2"
wasm-bindgen = "=0.2.61"
Build and deploy
You need to use the ssvmup tool to compile the Rust function into WebAssembly bytecode (i.e., the wasm file). Do the following and the compiled wasm file is in the pkg
directory.
$ ssvmup build
Upload the wasm file in the pkg
folder to the FaaS /api/executables
RPC service endpoint. Double check the .wasm
file name before you upload.
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/executables' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM-Description: watermark' \
--data-binary '@pkg/watermark_lib_bg.wasm'
Once the wasm file is deployed, the FaaS returns a wasm_id
for the deployment. You will use this wasm_id
to access the functions in the deployed wasm file later.
{"wasm_id":148,"wasm_sha256":"0xfb413547a8aba56d0349603a7989e269f3846245e51804932b3e02bc0be4b665","usage_key":"00000000-0000-0000-0000-000000000000","admin_key":"00xxxxxx-xxxx-xxxx-xxxx-4adc960fd2b8"}
Invoke the function via HTTP Post
You can now call the Rust function over the web. The RPC service endpoint is /api/multipart/run/wasm_id/function_name/bytes
where the wasm_id
is the ID for the wasm file we deployed, and the function_name
is the name of the function we want to invoke in the wasm file.
Notice the
multipart
in the service endpoint URL. Since the function takes multiple input arguments, we need to send in the request as a HTTP multipart request, with each part containing one argument. We are calling the/bytes
endpoint because the function returns aVec<u8>
value.
The HTTP request body parts are passed to the function as call arguments. The part name determines the sequence of the arguments. For example, the part named input_1
contains the first call argument, and the part named input_2
contains the second call argument, etc.
We put a string for the watermark text and an image file cat.png
into the request body as two parts. The HTTP response body contains the returned bytes from the function. The response body is saved to the tmp.png
file by the curl command.
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes' \
--header 'Content-Type: multipart/form-data' \
--form 'input_1=Meow world!' \
--form 'input_2=@test/cat.png' \
--output tmp.png
Open the tmp.png
file and you will see the watermark!
Use a webhook as function input
An important use case of FaaS is to trigger the function from another service, such as a messaging queue. In this case, the calling service often passes in an URL pointing to the data, instead of submitting the data itself in a HTTP request. With the Second State FaaS, you can also pass in a URL in the input, and the FaaS will fetch the data in the URL and pass it to the function as an argument.
It is important to note that the fetch URL’s multipart name must start with fetch_input
. If you just use the regular input
name, the URL itself will be passed as a string argument.
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes' \
--header 'Content-Type: multipart/form-data' \
--form 'input_1=Woof world!' \
--form 'fetch_input_2=https://www.secondstate.io/demo/dog.png' \
--output tmp.png
Open the tmp.png
file and you will see the watermark on the dog!
Web UI
On a static web page, you can use JavaScript to make an AJAX call to this FaaS function. The AJAX function posts a multipart
request. The first part is a text string of the watermark message, and the second part is an uploaded image file. Those two parts are passed to the FaaS function as two arguments. The AJAX response is binary data for the watermarked image (bytes
). The JavaScript displays the response image on the page.
var formData = new FormData();
formData.append('input_1', $('#input_1').val());
formData.append('input_2', $('#input_2')[0].files[0]);
$.ajax({
url: "https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes",
type: "post",
data : formData,
contentType: false,
processData: false,
xhrFields:{
responseType: 'blob'
},
success: function (data) {
const img_url = URL.createObjectURL(data);
$('#wm_img').prop('src', img_url);
}
});
What’s next
With the /multipart/
service endpoint and multipart HTTP Post requests, we can create functions that take multiple strings, byte arrays, and fetch_input URLs, as input arguments.
In the next article, we will shift our focus to the return value of the function. In particular, we would like to send the return value to another web service and create a process chain of serverless applications.