WebIO Env (with Mux)

Pkg.pkg"add WebIO#master JSExpr#master CSSUtil Mux WebSockets HTTP"
using HTTP
using WebSockets
using Sockets
using Mux
using Base64
using WebIO
using JSExpr

reference code for WebIO accepting an ip address to bind to

using HTTP
using WebSockets
using Sockets
using Mux
using Base64
using WebIO
using JSExpr
WebIO.providers_initialised

Define a WebIO coder to produce an iframe

0.3s
const WebThing = Union{Scope, Node}
is_node(value::WebThing) = true
is_node(value) = false

function webio_write(dome::WebThing, path::String)
  s = Scope()
  # hack for resizing the iframe from the inside...
  onimport(s,
        js"""function () {
            var frame = window.frameElement;
    				frame.style.height = '400px';
        }""")
  s.dom = dome
  open(path, "w") do io
     print(io, """
      <!doctype html>
      <html>
      <head>
      <meta charset="UTF-8">
      <script src="https://rawgit.com/JuliaGizmos/WebIO.jl/master/assets/webio/dist/bundle.js"></script>
      </head>
      <body>
      $(stringmime("text/html", s))
      </body>
      </html>
        """
     )
  end
  Dict(
    "content-type" => "text/html", 
    :kind => "iframe", 
    :metadata => nothing
  )
end
Coders.register_coder(
    "webio",
    is_node,
    webio_write
)
nothing
node(:h1, "Hello")

We need to override WebIO Mux provider because Mux doesn't allow to pass a custom ip address to bind to, and defaults to localhost (while we need 0.0.0.0).

0.4s
using WebSockets
import WebSockets.serve, WebSockets.ServerWS
import HTTP.HandlerFunction

struct WebSockConnection <: WebIO.AbstractConnection
    sock
end

function create_socket(req)
    sock = req[:socket]
    conn = WebSockConnection(sock)

    t = @async while isopen(sock)
        data = read(sock)

        msg = JSON.parse(String(data))
        WebIO.dispatch(conn, msg)
    end

    wait(t)
end

mk_response(d) = d
function mk_response(d::Dict)
  r = HTTP.Response(get(d, :status, 200))
  haskey(d, :body) && (r.body = d[:body])
  haskey(d, :headers) && (r.headers = d[:headers])
  return r
end

function http_handler(app)
  handler = HandlerFunction((req) -> mk_response(app.warez(req)))
  # handler.events["error"]  = (client, error) -> println(error)
  # handler.events["listen"] = (port)          -> println("Listening on $port...")
  return handler
end

function ws_handler(app)
  handler = WebSockets.WebsocketHandler((req, client) -> mk_response(app.warez((req, client))))
  return handler
end

function webio_serve2(app, addr, port)
    http = Mux.App(Mux.mux(
        Mux.defaults,
        app,
        Mux.notfound()
    ))

    websock = Mux.App(Mux.mux(
        Mux.wdefaults,
        Mux.route("/webio-socket", create_socket),
        Mux.wclose,
        Mux.notfound(),
    ))
		ws_server = ServerWS(http_handler(http), ws_handler(websock))
    serve(ws_server, addr, port, true)
end

A simple counter app to show the bi-directional communication (remember to change the ws port to the right mapped value.

1.9s
function counter(start=0)
    scope = Scope()
		onimport(scope,
        js"""function () {
						   console.log("imported!")
               var ws = new WebSocket("ws://localhost:32829/webio-socket")
    				   ws.onopen = function () {
                 WebIO.sendCallback = function (msg) {
                   ws.send(JSON.stringify(msg))
                 }
                 ws.onmessage = function (evt) {
                   WebIO.dispatch(JSON.parse(evt.data));
                 }
                 WebIO.triggerConnected()
               }
               ws.onclose = function (evt) { console.warn(evt) }
        }""")
  	# js init code
  	stringmime("text/html", scope)	
  
    # updates to this update the UI
    count = Observable(scope, "count", start)

    onjs(count, # listen on JavaScript
         JSExpr.@js x->this.dom.querySelector("#count").textContent = x)

    on(count) do n # listen on Julia
      println("GotSomething")  
      println(n > 0 ? "+"^n : "-"^abs(n))
    end

    btn(label, d) = dom"button"(
        label,
        events=Dict(
            "click" => JSExpr.@js () -> $count[] = $count[] + $d
        )
    )

    scope.dom = dom"div"(
        btn("increment", 1),
        btn("decrement", -1),
        dom"div#count"(string(count[])),
    )

    scope
end
c = counter(1)
nothing
app = page("/", req -> c)
@async webio_serve2(app, "0.0.0.0", 8889)
c
© 2018 Nextjournal GmbH