I was looking into webassembly again today in the hope of somehow using it to call Google Cloud Storage
using its own go SDK client libraries.
I got that to work and will have a followup post about it. ..but..
Along the way to set that up, to my surprise i found out that golang’s net/http clients actually work within browser wasm. This was really interesting and didn’t think it was possible due to the wasm sandbox in the browser
I only stumbled across that after reading Alessandro Segala's
excellent blog here:
I decided to implement that end-to-end and write a small sample on my own derived from his work and from Omri Cohen's
Run Go In The Browser Using WebAssembly.
Basically, this sample code here shows you how to compile and run a go-webassembly that does two things:
Both these are again derived from the the references above so this really isn’t anything i did…i just documented it as an end-to-end sample.
You can find the source here
Creating a go-webassembly app is nothing new…even i did one a couple weeks back that is outright impractical:
Note, this is about browser wasm …not wasm running in a proxy like Envoy
…that has a lot more capabilities and can surely make http or even gRPC api calls on its own:
What the issue i’m demonstrating here is browser wasm making an http call in go. Why is that so difficult? well, wasm in the browser is itself prohibited from making http requests….but….the following PR jL9Lyma0nmo allowed go’s net/http
package to call javascrpts fetch() transparently.
That is, whenever an http client in go is used in browswer, wasm, it internally calls fetch()
….its interesting that this workaround exists but i’m not complaining!
If you want to read up on how this works internally or the specifics of the streaming fetch capability, please see the documentation and the PR cited above…i’ll just focus on getting the impatient up and running…
Anyway, lets set this up so you can run locally
I’m using https
for the webserver which you will run and due to the certs, its just as easy if you setup a local redirect:
set in /etc/hosts
:
127.0.0.1 gcs.somedomain.com
Now, just compile the wasm if you want to
compile
git clone https://github.com/salrashid123/wasm_http_go.git
cd wasm_http_go/
GOOS=js GOARCH=wasm go build -o server/static/wasm/main.wasm main.go
Now run the webserver
cd server
go run server.go
Note that the webserver loads wasm
index.html
, see <script src="js/wasm.js" defer></script>
which inturn bootstraps the main.wasm
file:
'use strict';
const WASM_URL = 'wasm/main.wasm';
var wasm;
function init() {
const go = new Go();
if ('instantiateStreaming' in WebAssembly) {
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
})
} else {
fetch(WASM_URL).then(resp =>
resp.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
wasm = obj.instance;
go.run(wasm);
})
)
}
}
init();
Ok, lets access the browser. In an incognito window, goto https://gcs.somedomain.com:8080/static/
. Ignore the cert warning
Test base64 encoding with wasm:
PlainText
, click submitTest direct fetch or streaming.
The streaming one will read chunks of data and output it to javascript-land while the sync one will read the fetch results into memory before returning. It’d be better if you use streaming since you probably dont’ want to OOM wasm runtime.
We’re using plain go to compile into wasm…i tried to use tinygo
but i didn’t work
There would’ve been a small savings here interms of size:
$ GOOS=js GOARCH=wasm go build -o server/static/wasm/main.wasm main.go
$ ls -larth server/static/wasm/main.wasm
6.6M Mar 21 13:09 server/static/wasm/main.wasm
$ tinygo build -o server/static/wasm/main.wasm -target wasm ./main.go
4.3M Mar 21 13:07 server/static/wasm/main.wasm
This site supports webmentions. Send me a mention via this form.