One of the limitations of the WebAssembly VM is that it provides no built-in support for network applications. In the web browser or in Node.js, it is possible to call the host JavaScript to perform networking tasks. But of course, a JavaScript bridge is slow, highly dependent on the host platform (i.e., not portable), and requires the developer to use JavaScript. In the SSVM, we have a new way to do this.
The http_proxy
is a command you can invoke from your Rust function to access network resources from within the SSVM WebAssembly program. The SSVM executes the http_proxy
as a native program, and exchange data with the calling SSVM program via STDIN
and STDOUT
. To show how this works, let’s revisit our simple email function service.
A generic email function service
The generic email function service takes a JSON object as input and then invokes the sendgrid API to send the email. The input JSON contains the email’s from and to addresses, the subject text, the body’s MIME type and content, and the sendgrid API token to use for this transaction. The source code of the example application is available on GitHub. The Rust function is as follows. The Msg
struct models the input JSON object. The function first parses the input JSON object. It then creates a payload
JSON object that conforms to the sendgrid API specification. The Command::new("http_proxy")
statement calls the http_proxy
to make the sendgrid web service call, which we will cover the next.
#[derive(Serialize, Deserialize, Debug)]
struct Msg {
from: String,
token: String,
to: String,
subject: String,
mime: String,
body: String
}
#[wasm_bindgen]
pub fn send_email(s: &str) -> String {
let msg: Msg = serde_json::from_str(s).unwrap();
let payload = json!(
{
"personalizations": [{
"to": [{
"email": &msg.to
}]
}],
"from": {
"email": &msg.from
},
"subject":&msg.subject,
"content": [{
"type": &msg.mime,
"value": &msg.body
}]
});
let auth_header: String = "{\"Content-Type\": \"application/json\",\"authorization\": \"Bearer ".to_owned() + &msg.token + "\"}";
// Execute the command for the HTTP web service
let mut cmd = Command::new("http_proxy");
cmd.arg("post")
.arg("https://api.sendgrid.com/v3/mail/send")
.arg(auth_header);
cmd.stdin_u8vec(payload.to_string().as_bytes());
// Receives the HTTP response
let out = cmd.output();
if out.status != 0 {
println!("{}", str::from_utf8(&out.stderr).unwrap());
}
return str::from_utf8(&out.stdout).unwrap().to_string();
}
The http_proxy command
The http_proxy
command in send_email()
takes three arguments via the chained arg()
function calls.
- The first argument is the HTTP method. It could be
get
orpost
. - The second argument is the URL to access.
- The third argument is a JSON string for the HTTP headers. It is a JSON dictionary for header key and value pairs.
The HTTP request body is passed to the command via the cmd.stdin_u8vec()
function call. You can put an arbitrary byte array in the HTTP request body. The cmd.output()
function call executes the web request, and encapsulates the HTTP response in the return value out
.
- The
out.stdout
is the byte array of the HTTP response. - The
out.stderr
is the byte array of any error messagehttp_proxy
emits.
The send_email()
function returns the HTTP response from the sendgrid API.
Try it out
First, build the function via the ssvmup tool.
$ ssvmup build
Upload the WebAssembly file to the FaaS and get a wasm_id
to call later.
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/executables' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM-Description: send email' \
--data-binary '@pkg/send_email_lib_bg.wasm'
{"wasm_id":151,"wasm_sha256":"0xec9e4c7d01920f...644bed9bf7922","SSVM_Usage_Key":"00000000-0000-0000-0000-000000000000","SSVM_Admin_Key":"b425089...8bfa58e6"}
Now you can call the function to send an email!
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/run/151/send_email' \
--header 'Content-Type: text/plain' \
--data '{"from":"michael@secondstate.io", "token":"SG.xxx", "to":"juntao_yuan@yahoo.com", "subject":"This is a HTTP Proxy FaaS test", "mime":"text/plain", "body":"Hello Second State FaaS!"}'
The SG.xxx
token is a sendgrid authorization token that is associated with the from
email address. You can get a limited sendgrid plan for free.
Web UI
On a static web page, you can use JavaScript to make an AJAX call to this FaaS function. The JavaScript assembles a JSON object, posts it to the FaaS function, and displays the result.
jsonObj = {};
jsonObj['from'] = $('#from').val();
jsonObj['token'] = $('#token').val();
jsonObj['to'] = $('#to').val();
jsonObj['subject'] = $('#subject').val();
jsonObj['mime'] = $('#mime').val();
jsonObj['body'] = $('#body').val();
$.ajax({
url: "https://rpc.ssvm.secondstate.io:8081/api/run/151/send_email",
type: "post",
data : JSON.stringify(jsonObj),
contentType: "text/plain",
processData: false,
success: function (data) {
if (data) {
$('#result').html(data);
} else {
$('#result').html("The email is sent to " + jsonObj['to'] + ". Please check your INBOX.");
}
}
});
What’s next
The use of commands is a unique feature that makes the Second State VM extensible. It is very important for server-side applications. The Second State FaaS supports high performance AI services through the command API.