I’m @aq
HELLO!
5 facts about @aq
Follow
@catskillsconf
Oct 23-25
Ruby, JS, Go Performance etc
but you can hire me!
The Story of
a Yak Shave
Some node.js options:
not precise or 1:1; poor font handling
node-canvas
almost there, but more geared to GUI
applications
node-webkit
Have the ability to reuse existing code and
knowledge about production
I wanted to see if go was
possible
aka Chromium Embedded Framework
Enter and cef2go
chromium
C & C++ API
cef2go (cgo)
C & C++ API
cef2go
your go app
your js app
Service Request/
Response
chromium
JS Exec/
Response
1 for i := 0; i < 3; i++ {
2 browser := cef.CreateBrowser(browserSettings, url, true)
3 browser.ExecuteJavaScript("console.log('we outchea');", "sup.js", 1)
4 }
5
3 browser.ExecuteJavaScript("console.log('we outchea');", "sup.js", 1)
github.com/paperlesspost/chromefarm
Solve it with AJAX
10 func main() {
12 farm := chromefarm.NewFarm()
14 // This serves the postback.html for the browser to load
15 http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
17 http.ServeFile(res, req, "postback.html")
18 })
19 // this handles the callback from JS/AJAX
20 http.HandleFunc("/callback", func(res http.ResponseWriter, req *http.Request) {
21 message := req.FormValue("message")
22 log.Println("Received message from browser: %s", message)
25 })
26 farm.OnReady(func() {
29 for i := 0; i < 2; i++ {
30 browser := farm.CreateBrowser(url)
31 browser.ExecuteJavaScript(fmt.Sprintf("console.log('sup %d');", i), "eva
32 }
33 })
34 farm.Start(":3000")
36 }
7 <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
8 <script type="text/javascript">
9 $.post('/callback', {message: "I've loaded successfully"}, function(resp) {
10 console.log(resp);
11 });
12 </script>
13 </body>
This has to get MORE awesome
Hmmm
24 extCode := `
25 var cef2go;
26 if (!cef2go) {
27 cef2go = {};
28 }
29 (function() {
30 cef2go.callback = function() {
31 native function callback();
32 return callback.apply(this, arguments);
33 }
34 })();
35 `
36 C.cef_register_extension(CEFString("v8/cef2go"), CEFString(extCode), handler)
C C
cef.callback('myGoCallback', "any", "type", 0, "args", true)
1 // cef.callback('myGoCallback', "any", "type", 0.0, 13, true)
2 cef.RegisterV8Callback("myGoCallback", cef.V8Callback(func(args []*cef.V8Value) {
3 arg0 := args[0].ToString() //=> "any"
4 arg1 := args[1].ToString() //=> "type"
5 arg2 := args[2].ToFloat32() //=> 0.0
6 arg3 := args[3].ToInt32() //=> 13
7 arg4 := args[4].ToBool() //=> true
8 // I'm in go now #yolo
9 }))
10
BUt WHY?!
Or a JS app that takes advantage of Go’s
concurrency + tools
A Go app that makes
use of the browser + JS
I mean the real reason is because its an
epic and beautiful hack.
@aq
quirkey.com
github.com/quirkey
THANKS

Chromium Embedded Framework + Go at Brooklyn JS

  • 1.
  • 2.
  • 4.
  • 6.
    Ruby, JS, GoPerformance etc but you can hire me!
  • 8.
    The Story of aYak Shave
  • 10.
  • 11.
    not precise or1:1; poor font handling node-canvas
  • 12.
    almost there, butmore geared to GUI applications node-webkit
  • 14.
    Have the abilityto reuse existing code and knowledge about production I wanted to see if go was possible
  • 15.
    aka Chromium EmbeddedFramework Enter and cef2go
  • 16.
    chromium C & C++API cef2go (cgo)
  • 17.
    C & C++API cef2go your go app your js app Service Request/ Response chromium JS Exec/ Response
  • 18.
    1 for i:= 0; i < 3; i++ { 2 browser := cef.CreateBrowser(browserSettings, url, true) 3 browser.ExecuteJavaScript("console.log('we outchea');", "sup.js", 1) 4 } 5
  • 19.
  • 20.
  • 21.
    10 func main(){ 12 farm := chromefarm.NewFarm() 14 // This serves the postback.html for the browser to load 15 http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { 17 http.ServeFile(res, req, "postback.html") 18 }) 19 // this handles the callback from JS/AJAX 20 http.HandleFunc("/callback", func(res http.ResponseWriter, req *http.Request) { 21 message := req.FormValue("message") 22 log.Println("Received message from browser: %s", message) 25 }) 26 farm.OnReady(func() { 29 for i := 0; i < 2; i++ { 30 browser := farm.CreateBrowser(url) 31 browser.ExecuteJavaScript(fmt.Sprintf("console.log('sup %d');", i), "eva 32 } 33 }) 34 farm.Start(":3000") 36 } 7 <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script> 8 <script type="text/javascript"> 9 $.post('/callback', {message: "I've loaded successfully"}, function(resp) { 10 console.log(resp); 11 }); 12 </script> 13 </body>
  • 22.
    This has toget MORE awesome Hmmm
  • 23.
    24 extCode :=` 25 var cef2go; 26 if (!cef2go) { 27 cef2go = {}; 28 } 29 (function() { 30 cef2go.callback = function() { 31 native function callback(); 32 return callback.apply(this, arguments); 33 } 34 })(); 35 ` 36 C.cef_register_extension(CEFString("v8/cef2go"), CEFString(extCode), handler)
  • 24.
  • 25.
  • 26.
    1 // cef.callback('myGoCallback',"any", "type", 0.0, 13, true) 2 cef.RegisterV8Callback("myGoCallback", cef.V8Callback(func(args []*cef.V8Value) { 3 arg0 := args[0].ToString() //=> "any" 4 arg1 := args[1].ToString() //=> "type" 5 arg2 := args[2].ToFloat32() //=> 0.0 6 arg3 := args[3].ToInt32() //=> 13 7 arg4 := args[4].ToBool() //=> true 8 // I'm in go now #yolo 9 })) 10
  • 28.
  • 29.
    Or a JSapp that takes advantage of Go’s concurrency + tools A Go app that makes use of the browser + JS
  • 30.
    I mean thereal reason is because its an epic and beautiful hack.
  • 31.

Editor's Notes

  • #4 1) grew up less then a mile from here which leads me to
  • #5 2) KINGSTON now repping the HV and live in a 350 year old house, save the date Oct 23rd: Catskills Conf is coming
  • #6 3) Used to be the Chief Taco Officer at Paperless Post, now I'm the Chief Scientist and get to work on things like I'm about to show you
  • #7 4) Also been doing JS/Ruby/Go Perfomance and Organization consulting so hire me
  • #8 5) I host a weekly podcast about Music, Food and Programming with MRB called Beats, Rye & Types (check out the episode with Jed)
  • #10 Started with the need to take a JS application that relied on canvas to turn data into images. It's important to note that we actually needed a full browser and not just JS - we needed canvas and a way to get that rendered buffer from canvas
  • #13 I’m sure there are other options and newer ones since we started this project a year ago.
  • #14 that damn gopher
  • #15 I really wanted to figure out a way to do this in Go because I've been writing a lot of Go and wanted to be able to reuse the communication, monitoring and tooling that I had already created.
  • #16 Explored some existing webkit wrappers but nothing really worked well, until I found Chromium embedded framework (CEF) and cef2go.
  • #17 cef2go takes advantage of the fact that cef exposes a simple C api/bindings and go has amazing support for embedding utilizing c code through cef2go cef2go was in a sort of bad state, so I spent a bunch of time cleaning it up and getting it working on linux
  • #18 eventually I got to a place where I could boot up a "browser" and execute JS in it.
  • #19  The cool thing is that because of Go's concurrency model it took no extra work to just loop and create many browsers. In this case, the idea would be 1-per CPU.
  • #20 The next part was getting data back from JS into the go process and back to the client of the application. The problem is ExecuteJavascript is async so you can't just get the return value.
  • #21 The "normal" way to do this was to set up an http server and handle AJAX post calls from the JS. This actually was pretty easy, I combined the cef boot + http setup in a little wrapper. Created a little library to manage this called chromefarm.
  • #22 It looked something like: Go code JS code
  • #23 But i knew there had to be SOME way to call directly back to go from JS.
  • #24 Turns out, through some roundabout ways, you can create JS methods bound to the V8 instance in CEF that are backed by NATIVE methods. Native in this case means a C function that is compiled alongside CEF, but using the magic of CGO that method can actually be backed by a Go function. SO
  • #26 Creating these native methods was sort of a pain though, so using the property of JS that allows methods to have variable argument arity we can bind a single method as a "native" method that can then call any "registered" method on the go side. In the JS this looks like: cef.callback('myGoCallback', "any", "type", 0, "args", true)
  • #27 And on the go side we just have to register what we want to do with this callback: we also have to translate the individual types to the native types we want in Go. But this actually works, and means we can pass data back and forth from a running browser to Go.
  • #29 For us it enabled a really stable and fast platform for turning canvas based renders into images. This is running in production today and has already handled requests in the 100s of millions
  • #30 But ton's of possibilities, it could bring the power of anything you want to do in a modern chrome browser to go and really the other way around, too. You could have an app that's primarily JS but run in a cef shell and have access to all the powerful system level (and concurrent code) that go can provide.