janestreet / bonsai

A library for building dynamic webapps, using Js_of_ocaml
MIT License
368 stars 39 forks source link

Implementing RPC call w/ Effect.of_deferred_fun #44

Closed DarioHett closed 4 months ago

DarioHett commented 4 months ago

I've tried to integrate the double RPC example into #42 with little success. Implementing the same call with Async.Rpc in a separate binary works. There is no activity on the browser side apart from incrementing the box, so I assume it has to do with how Async_js schedules?

open! Core
open! Bonsai_web
open Async_kernel
open! Bonsai.Let_syntax

let double_rpc =
  Async_js.Rpc.Rpc.create
    ~name:"double"
    ~version:0
    ~bin_query:Int.bin_t
    ~bin_response:Int.bin_t   
    ~include_in_error_count:Only_on_exn
;;

let do_some_compute =
  let open Async_kernel in
  let persistent_connection = Async_js.Persistent_connection.Rpc.create_from_uri ~server_name:"Double_service" ~connect:(fun uri -> Async_js.Rpc.Connection.client ~uri:uri ()) (fun () -> return (Result.Ok (Uri.of_string "127.0.0.1:8080"))) in
  let f i = 
    let%bind conn = Async_js.Persistent_connection.Rpc.connected persistent_connection in
    let%map x = Async_js.Rpc.Rpc.dispatch_exn double_rpc conn i in
    x
  in
  Effect.of_deferred_fun f
;;

let main =
  let%sub number, set_number = Bonsai.state 0 in
  let%sub text =
    let%sub status =
      Bonsai.Edge.Poll.effect_on_change
        Bonsai.Edge.Poll.Starting.empty
        ~effect:(Value.return do_some_compute)
        ~equal_input:[%equal: int]
        number
    in
    match%arr status with
    | None -> Vdom.Node.text "Not fetched"
    | Some i -> Vdom.Node.textf "Fetched: %d" i
  in
  let%arr text = text
  and number = number
  and set_number = set_number in
  Vdom.Node.div
    [ text
    ; Vdom.Node.button
        ~attrs:[ Vdom.Attr.on_click (fun _ -> set_number (number + 1)) ]
        [ Vdom.Node.textf "%d" number ]
    ]
;;

let run () =
  Async_js.init ();
  let () = Bonsai_web.Start.start main
  in
  Deferred.return ()

let () = don't_wait_for (run ())

There are no issues, if I implement a Http call, eg let f i = Async_js.Http.get "http://httpbin.org/get" |> Deferred.Or_error.ok_exn.

//edit: Added Async_kernel and double_rpc for compilation.

TyOverby commented 4 months ago

Is anything happening in the browser dev tools network page?

DarioHett commented 4 months ago

No activity at all, I changed the connection to:

let%bind conn = Async_js.Rpc.Connection.client_exn ~uri:(Uri.of_string "http://127.0.0.1:8080") () in

At least now there is activity, 403 Forbidden on the client/browser and on the server (using async_rpc_websocket; no success with an async_rpc implementation so far).

2024-07-16 22:28:55.559567+02:00 Error ("Failed to validate apparent websocket request"("No scheme"null)((headers((Host 127.0.0.1:8080)(User-Agent"Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0")(Accept */*)(Accept-Language"en-US,en;q=0.5")(Accept-Encoding"gzip, deflate, br, zstd")(Sec-WebSocket-Version 13)(Origin null)(Sec-WebSocket-Extensions permessage-deflate)(Sec-WebSocket-Key C35+MJmncflrasLvcZ7IVw==)(Connection"keep-alive, Upgrade")(Sec-Fetch-Dest empty)(Sec-Fetch-Mode websocket)(Sec-Fetch-Site cross-site)(Pragma no-cache)(Cache-Control no-cache)(Upgrade websocket)))(meth GET)(scheme())(resource /)(version HTTP_1_1)(encoding Unknown)))
TyOverby commented 4 months ago

Ah yeah, I think that you should be using ws: as the protocol, but it sees http and translates it to ws: for you (https gets translated to wss).

Can you paste your server code too?

TyOverby commented 4 months ago

The rpc_chat is an example of a (hopefully) working front-end and back-end using async_rpc over websockets; if you can get that building, then it might be easier to find out what's going on with your code

DarioHett commented 4 months ago

I reused the RPC server set up from the basic examples: rpc_server and extended the implementations with the double_rpc mentioned above. The main surprise was no activity whatsoever, whereas moving to RPC via websockets at least gave the 403.

The rpc_chat works fine, and I'll use it to fix the code on my end. Is RPC via websockets generally preferred for bonsai?

TyOverby commented 4 months ago

Is RPC via websockets generally preferred for bonsai?

RPC via websockets is preferred at Jane Street, where almost all of our native code uses Async_rpc. Because of that, we've built several helpers in Bonsai_web.Rpc_effect. Beyond that, there's no preference baked into the framework

TyOverby commented 4 months ago

I'm going to close this issue, but if you find out what was going wrong on your server, please let me know; I'll document it so hopefully it doesn't happen to other people